unknown vs any: в чем разница и когда что использовать в TypeScript?
Ключевое различие: безопасность типов
Основное отличие any от unknown в TypeScript — это уровень контроля типов. any полностью отключает проверку типов, делая переменную "вседозволенной". unknown же, напротив, требует явной проверки перед использованием. Это делает unknown типобезопасной альтернативой any.
Как работает any
any — это "выйти из TypeScript". Вы можете делать с ним всё что угодно: вызывать методы, обращаться к свойствам, присваивать любым переменным. Компилятор не будет предупреждать об ошибках, но они проявятся в рантайме.
let value: any = "Hello";
console.log(value.toUpperCase()); // "HELLO" - работает
value = 42;
console.log(value.toUpperCase()); // Ошибка в рантайме! value.toUpperCase is not a function
value.someMethod(); // Тоже ошибка в рантайме, но компилятор молчит
Как работает unknown
unknown — это "я не знаю тип, но давайте проверим". Вы не можете использовать переменную типа unknown без явной проверки или приведения типа. Компилятор заставит вас убедиться, что операция безопасна.
let value: unknown = "Hello";
// value.toUpperCase(); // Ошибка компиляции: Object is of type 'unknown'
if (typeof value === "string") {
console.log(value.toUpperCase()); // "HELLO" - безопасно, внутри проверки
}
// Или с приведением:
console.log((value as string).toUpperCase()); // "HELLO" - но осторожно, если приведение неверное
Когда использовать any
- Миграция с JavaScript: когда постепенно добавляете типы в старый проект,
anyможет быть временным решением. - Прототипирование: для быстрых экспериментов, когда типы только мешают.
- Специфические случаи: например, при работе с
eval()или динамическими импортами, где тип действительно неизвестен и неважен.
Когда использовать unknown
- Данные из внешних источников: API, JSON-парсинг, ввод пользователя — всегда используйте
unknown. - Библиотеки без типов: когда подключаете JS-библиотеку без определений типов.
- Обобщенные функции: если функция принимает данные любого типа, но должна их обрабатывать безопасно.
Правильная обработка unknown
Вместо "кучи проверок" используйте type guards (защитники типа) — функции, которые проверяют и сужают тип:
function isString(value: unknown): value is string {
return typeof value === "string";
}
function processUserData(data: unknown) {
if (isString(data)) {
// Здесь data — точно string
console.log(data.toUpperCase());
} else if (Array.isArray(data)) {
// Здесь data — точно массив
data.forEach(item => console.log(item));
} else {
console.log("Неизвестный тип данных");
}
}
Практический пример: работа с API
async function fetchUserData(id: number): Promise<unknown> {
const response = await fetch(`/api/users/${id}`);
return response.json(); // Мы не знаем, что придет
}
async function displayUserName(id: number) {
const data = await fetchUserData(id);
// Безопасная обработка
if (typeof data === "object" && data !== null && "name" in data) {
const userData = data as { name: string };
console.log(`User name: ${userData.name}`);
} else {
console.error("Некорректный формат данных");
}
}
Итог
any— для быстрого кода, когда типы не важны (но это рискованно).unknown— для безопасного кода, когда тип неизвестен, но его нужно проверить.- Всегда предпочитайте
unknownвместоanyдля внешних данных. - Используйте type guards и утверждения типов (
as) для работы сunknown.
Помните: TypeScript создан, чтобы ловить ошибки на этапе компиляции. unknown помогает это делать, а any — прячет проблемы до рантайма.
Альтернативный подход: утилитарные функции для работы с unknown
Вместо того чтобы писать проверки в каждом месте, можно создать набор вспомогательных функций, которые упростят работу с unknown. Это сделает код чище и переиспользуемее.
Утилиты для валидации
// Функция-валидатор с возвратом результата
function safeString(value: unknown): string | null {
return typeof value === "string" ? value : null;
}
function safeNumber(value: unknown): number | null {
return typeof value === "number" && !isNaN(value) ? value : null;
}
// Использование
function processData(data: unknown) {
const name = safeString(data);
if (name) {
console.log(`Привет, ${name}!`);
}
const age = safeNumber(data);
if (age !== null) {
console.log(`Возраст: ${age}`);
}
}
Деструктуризация с проверкой
Для объектов из API удобно использовать деструктуризацию с проверкой:
function extractUserInfo(data: unknown): { name?: string; email?: string } {
if (typeof data !== "object" || data === null) return {};
const obj = data as Record<string, unknown>;
return {
name: safeString(obj.name),
email: safeString(obj.email)
};
}
// Теперь обработка проста:
const user = extractUserInfo(apiResponse);
if (user.name) {
console.log(user.name.toUpperCase());
}
Преимущества подхода
- Переиспользование: функции-валидаторы можно использовать в разных частях проекта.
- Читаемость: код становится понятнее, чем куча вложенных
if. - Тестируемость: логику валидации легко тестировать отдельно.
- Безопасность: вы контролируете, какие типы допускаете, и избегаете неожиданных ошибок.
Этот подход особенно полезен в больших проектах, где данные из API проходят через множество функций. Вместо того чтобы везде писать typeof проверки, вы создаете слой валидации, который делает код надежным и поддерживаемым.
Ответов пока нет.
Войдите чтобы оставить ответ