Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Работа с типом 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");
}
Лучшие практики
- Избегай any — используй unknown для безопасности
- Всегда проверяй тип перед использованием неизвестных данных
- Создавай переиспользуемые type guards для сложных типов
- Используй валидаторы (Zod, Yup) для больших объектов
- Будь явным — не пиши
value as SomeTypeбез проверки - Документируй assumptions — какие поля ожидает функция
Заключение
Работа с unknown требует больше кода, но обеспечивает безопасность типов при работе с данными, полученными из API, JSON, пользовательского ввода. Type guards — это основной инструмент для трансформации unknown в конкретные типы в TypeScript.