← Назад к вопросам

Как из Unknown получить другой тип?

1.0 Junior🔥 122 комментариев
#TypeScript

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Работа с типом unknown в TypeScript

unknown — это безопасный тип в TypeScript, который требует явной проверки перед использованием. Это противоположность any, которая отключает все проверки типов. Чтобы получить конкретный тип из unknown, нужно использовать type guards (охранители типов).

Почему unknown безопаснее any

// Плохо: any отключает все проверки
const value: any = someFunction();
value.toUpperCase(); // no error, но может быть ошибка в runtime

// Хорошо: unknown требует проверки типа
const value: unknown = someFunction();
value.toUpperCase(); // ошибка TypeScript: Object is of type "unknown"

Способ 1: typeof guard

Для примитивных типов

const value: unknown = JSON.parse(userInput);

// typeof guard
if (typeof value === "string") {
  console.log(value.toUpperCase()); // value: string
}

if (typeof value === "number") {
  console.log(value.toFixed(2)); // value: number
}

if (typeof value === "boolean") {
  console.log(!value); // value: boolean
}

if (typeof value === "object" && value !== null) {
  console.log(Object.keys(value)); // value: object (но не null)
}

Практический пример

function processValue(value: unknown) {
  if (typeof value === "string") {
    return value.trim().toUpperCase();
  }
  
  if (typeof value === "number") {
    return value * 2;
  }
  
  if (Array.isArray(value)) {
    return value.length;
  }
  
  return "unknown type";
}

processValue("hello"); // "HELLO"
processValue(42); // 84
processValue([1, 2, 3]); // 3

Способ 2: instanceof guard

Для объектов и классов

class User {
  constructor(public name: string, public age: number) {}
  
  greet() {
    return `Hello, ${this.name}`;
  }
}

const value: unknown = new User("John", 30);

if (value instanceof User) {
  console.log(value.greet()); // value: User
  console.log(value.age); // доступны все свойства
}

// Работает и с встроенными типами
if (value instanceof Date) {
  console.log(value.getTime());
}

if (value instanceof RegExp) {
  console.log(value.test("string"));
}

if (value instanceof Error) {
  console.log(value.message);
}

Способ 3: Custom type guard функции

Функции с предикатом типа (type predicate)

interface User {
  name: string;
  age: number;
}

// Type predicate: value is User
function isUser(value: unknown): value is User {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value &&
    "age" in value &&
    typeof (value as User).name === "string" &&
    typeof (value as User).age === "number"
  );
}

const data: unknown = JSON.parse(apiResponse);

if (isUser(data)) {
  console.log(data.name); // data: User
  console.log(data.age);
}

Реальный пример: валидация API ответа

interface ApiResponse {
  status: "success" | "error";
  data?: unknown;
}

function isApiResponse(value: unknown): value is ApiResponse {
  if (typeof value !== "object" || value === null) return false;
  
  const obj = value as Record<string, unknown>;
  
  return (
    typeof obj.status === "string" &&
    ["success", "error"].includes(obj.status)
  );
}

async function fetchData() {
  const response = await fetch("/api/data");
  const json: unknown = await response.json();
  
  if (isApiResponse(json)) {
    console.log(json.status); // json: ApiResponse
    console.log(json.data);
  }
}

Способ 4: Discriminated Unions

Для сложных структур данных

type Result = 
  | { type: "success"; data: string }
  | { type: "error"; error: string }
  | { type: "loading" };

function isResult(value: unknown): value is Result {
  return (
    typeof value === "object" &&
    value !== null &&
    "type" in value &&
    ["success", "error", "loading"].includes((value as any).type)
  );
}

const result: unknown = someFunction();

if (isResult(result)) {
  switch (result.type) {
    case "success":
      console.log(result.data); // string
      break;
    case "error":
      console.log(result.error); // string
      break;
    case "loading":
      console.log("Loading...");
  }
}

Способ 5: Zod, Yup для валидации

Для больших объектов — используй валидатор

import { z } from "zod";

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  age: z.number().min(0).max(150).optional(),
});

type User = z.infer<typeof UserSchema>;

const data: unknown = apiResponse;

try {
  const validatedUser = UserSchema.parse(data); // validatedUser: User
  console.log(validatedUser.name);
} catch (error) {
  console.error("Invalid user data:", error.errors);
}

Или с safeParse (более безопасно)

const result = UserSchema.safeParse(data);

if (result.success) {
  const user = result.data; // user: User
  console.log(user.name);
} else {
  console.error(result.error.issues);
}

Способ 6: Assertion функции

Для случаев, когда ты уверен в типе

function assertIsUser(value: unknown): asserts value is User {
  if (!isUser(value)) {
    throw new Error("Value is not a User");
  }
}

const data: unknown = getUser();
assertIsUser(data);

// После этой строки TypeScript знает, что data: User
console.log(data.name);

Практический пример: обработка JSON

interface Post {
  id: number;
  title: string;
  content: string;
  author: { name: string; email: string };
}

function isPost(value: unknown): value is Post {
  if (typeof value !== "object" || value === null) return false;
  
  const obj = value as Record<string, unknown>;
  
  return (
    typeof obj.id === "number" &&
    typeof obj.title === "string" &&
    typeof obj.content === "string" &&
    typeof obj.author === "object" &&
    obj.author !== null &&
    typeof (obj.author as any).name === "string" &&
    typeof (obj.author as any).email === "string"
  );
}

async function getPost(id: number): Promise<Post | null> {
  const response = await fetch(`/api/posts/${id}`);
  const json: unknown = await response.json();
  
  if (isPost(json)) {
    return json;
  }
  
  throw new Error("Invalid post data");
}

Лучшие практики

  1. Избегай any — используй unknown для безопасности
  2. Всегда проверяй тип перед использованием неизвестных данных
  3. Создавай переиспользуемые type guards для сложных типов
  4. Используй валидаторы (Zod, Yup) для больших объектов
  5. Будь явным — не пиши value as SomeType без проверки
  6. Документируй assumptions — какие поля ожидает функция

Заключение

Работа с unknown требует больше кода, но обеспечивает безопасность типов при работе с данными, полученными из API, JSON, пользовательского ввода. Type guards — это основной инструмент для трансформации unknown в конкретные типы в TypeScript.

Как из Unknown получить другой тип? | PrepBro