В чем разница между типами any и unknown в TypeScript?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между any и unknown в TypeScript
Это один из самых важных различий в TypeScript. any и unknown кажутся одинаковыми, но имеют противоположное поведение. Вот ключевая разница: any отключает проверку типов, unknown требует проверки типов.
Быстрое сравнение
| Характеристика | any | unknown |
|---|---|---|
| Принимает любой тип | Да | Да |
| Можно присваивать любому типу | Да | Нет |
| Можно вызывать методы без проверки | Да | Нет |
| Требует type guard | Нет | Да |
| Безопасен | Нет | Да |
any - "Отключить проверку типов"
any означает: "Я не знаю тип, и мне не важно. TypeScript, не проверяй".
let value: any = "hello";
value.toUpperCase(); // OK - работает
value.map(x => x); // OK - работает, хотя это строка!
value.nonExistentMethod(); // OK - работает (но упадёт в runtime!)
const result: string = value; // OK - any присваивается любому типу
const result: number = value; // OK - any присваивается любому типу
С any TypeScript не защищает тебя:
function processData(data: any) {
data.forEach(item => item.name); // No error!
// Но если data - не массив, упадёт в runtime
}
processData("not an array"); // TypeError в runtime
unknown - "Неизвестный тип, проверь перед использованием"
unknown означает: "Я не знаю тип, но TypeScript будет требовать проверку перед использованием".
let value: unknown = "hello";
value.toUpperCase(); // ERROR - нужно проверить тип!
value.map(x => x); // ERROR - нужно проверить тип!
const result: string = value; // ERROR - unknown не присваивается строке
С unknown нужна проверка типа (type guard):
let value: unknown = "hello";
if (typeof value === "string") {
value.toUpperCase(); // OK - теперь TypeScript знает это строка
}
if (Array.isArray(value)) {
value.forEach(item => item); // OK - теперь это массив
}
Пример: обработка API ответа
Плохо - с any:
const data: any = await fetch('/api/user').then(r => r.json());
// Может привести к runtime ошибкам
data.user.name; // может быть undefined
data.items.forEach(item => item.id); // может быть не массивом
Хорошо - с unknown:
const data: unknown = await fetch('/api/user').then(r => r.json());
if (typeof data === 'object' && data !== null && 'user' in data) {
console.log(data.user.name); // безопасно
}
if (Array.isArray(data)) {
data.forEach(item => console.log(item.id));
}
Type guards для unknown
Тип гард - это функция, которая проверяет тип:
// Проверка примитивных типов
let value: unknown;
if (typeof value === 'string') {
value.toLowerCase(); // OK
}
if (typeof value === 'number') {
value.toFixed(2); // OK
}
if (typeof value === 'boolean') {
value === true; // OK
}
Проверка объектов:
interface User {
id: number;
name: string;
}
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'name' in obj &&
typeof obj.id === 'number' &&
typeof obj.name === 'string'
);
}
const data: unknown = { id: 1, name: 'John' };
if (isUser(data)) {
console.log(data.name); // OK
}
Проверка массивов:
const data: unknown = ['a', 'b', 'c'];
if (Array.isArray(data)) {
data.forEach(item => console.log(item)); // OK
}
Практический пример - обработка ошибок
// Плохо
try {
// код
} catch (error: any) {
console.log(error.message); // может упасть!
}
// Хорошо
try {
// код
} catch (error: unknown) {
if (error instanceof Error) {
console.log(error.message); // OK
} else if (typeof error === 'string') {
console.log(error);
} else {
console.log('Unknown error');
}
}
any vs unknown: когда использовать
Никогда не используй any - это ломает весь смысл TypeScript.
// Плохо
function handleData(data: any) {
// TypeScript не может помочь
}
// Хорошо
function handleData(data: unknown) {
// TypeScript требует проверки
if (typeof data === 'object') {
// теперь безопасно
}
}
Используй unknown когда:
- Получаешь данные из API
- Обрабатываешь ошибки
- Работаешь с внешними библиотеками
- Получаешь JSON
const apiResponse: unknown = await api.getUser();
const errorData: unknown = catch(error);
const externalData: unknown = someLibrary.getData();
const jsonData: unknown = JSON.parse(jsonString);
Почему any опасен
let value: any = 42;
value = "hello"; // OK
value = { name: "John" }; // OK
value.nonExistentMethod(); // OK во время написания, ОШИБКА в runtime
// any распространяется:
const result = value.toUpperCase(); // result: any
result.nonExistentMethod(); // OK - но это ошибка!
unknown требует явной проверки:
let value: unknown = 42;
value = "hello"; // OK
value = { name: "John" }; // OK
value.nonExistentMethod(); // ERROR - нужна проверка!
if (typeof value === 'string') {
value.toUpperCase(); // OK - проверили
}
Стольная типизация (strict: true)
С strict: true в tsconfig.json:
- Параметры без типа считаются unknown
- Методы без явного возврата считаются unknown
- Требуется более строгая проверка
// С strict: true
// Плохо
function process(data) { // ERROR: implicit any
}
// Хорошо
function process(data: unknown) {
if (typeof data === 'object') {
// OK
}
}
Итог
- any - отключает проверку типов. Не используй.
- unknown - требует проверки типов. Используй.
Правило простое: если не знаешь тип, используй unknown и добавь type guard. Это безопаснее и заставляет тебя подумать о возможных значениях.
TypeScript создан, чтобы ловить ошибки. any обходит его защиту. unknown работает с его защитой.