Ответ
Type Guard (защита типа) в TypeScript — это выражение, которое выполняет проверку во время выполнения (runtime) и позволяет компилятору сузить тип переменной внутри определенной области видимости (например, внутри блока if). Это ключевой механизм для безопасной работы с union-типами, unknown или any.
Основные виды Type Guards:
-
typeofGuard: Для примитивных типов.function processValue(value: string | number) { if (typeof value === 'string') { // Здесь TypeScript знает, что value — строка console.log(value.toUpperCase()); } else { // Здесь value — число console.log(value.toFixed(2)); } } -
instanceofGuard: Для проверки принадлежности к классу.class ApiError extends Error { statusCode: number; constructor(message: string, statusCode: number) { super(message); this.statusCode = statusCode; } } async function handleError(error: Error | ApiError) { if (error instanceof ApiError) { // Теперь мы знаем, что это ApiError console.error(`API Error ${error.statusCode}: ${error.message}`); // Можно безопасно обратиться к error.statusCode } else { console.error(`Generic Error: ${error.message}`); } } -
Пользовательский Type Guard (User-Defined Type Guard): Функция, возвращающая предикат типа (
parameterName is Type).interface Cat { meow(): void; } interface Dog { bark(): void; } // Функция-защитник. Она говорит TypeScript: "Если я верну true, то pet — это Cat" function isCat(pet: Cat | Dog): pet is Cat { return (pet as Cat).meow !== undefined; // Или: return 'meow' in pet; } function playWithPet(pet: Cat | Dog) { if (isCat(pet)) { pet.meow(); // OK // pet.bark(); // Ошибка: Свойство 'bark' не существует у типа 'Cat' } else { pet.bark(); // TypeScript понимает, что здесь pet — Dog } }
Зачем это нужно в Node.js-разработке? Type Guards делают код типобезопасным при работе с внешними данными (парсинг JSON, валидация входящих HTTP-запросов), обработке ошибок разных типов или при использовании библиотек, возвращающих unknown.
Ответ 18+ 🔞
Слушай, а вот эта штука — Type Guard — это вообще пиздец как удобно, когда ты в TypeScript'е работаешь. Представь: у тебя переменная, а она может быть чем угодно — строкой, числом, каким-то твоим интерфейсом. И компилятор, блядь, на тебя смотрит как на идиота и говорит: «Ну я-то не знаю, что у тебя там сейчас, так что не дам вызвать .toUpperCase(), потому что вдруг это число?». И ты сидишь и думаешь: «Ёпта, я же вижу, что это строка!».
Вот тут-то и вырубают type guards. Это такие проверки в рантайме, которые компилятору на ушко шепчут: «Расслабься, братан, тут всё типобезопасно, я проверил». И компилятор успокаивается и сужает тип в этой конкретной области.
Какие они бывают, эти защитники?
-
typeofGuard: Самый простой, для примитивов. Как будто спрашиваешь у переменной паспорт.function processValue(value: string | number) { if (typeof value === 'string') { // А вот тут TypeScript уже охуел от твоей проницательности и разрешает всё console.log(value.toUpperCase()); // Всё ок, value — строка } else { // А сюда уже попадёт только число, так что можно его округлять console.log(value.toFixed(2)); } } -
instanceofGuard: Когда нужно понять, от какого класса твой объект. Типа «ты чей, мальчик?».class ApiError extends Error { statusCode: number; constructor(message: string, statusCode: number) { super(message); this.statusCode = statusCode; } } async function handleError(error: Error | ApiError) { if (error instanceof ApiError) { // Всё, попался, пидарас шерстяной! Теперь ясно, что это наша кастомная ошибка console.error(`API Error ${error.statusCode}: ${error.message}`); // И можно спокойно лезть в error.statusCode, доверия — ебать ноль, но типобезопасно! } else { console.error(`Generic Error: ${error.message}`); } } -
Пользовательский Type Guard: Вот это уже высший пилотаж, когда стандартных проверок не хватает. Ты пишешь свою функцию, которая возвращает не просто
boolean, а предикат типа (parameterName is Type). Это как дать компилятору официальную справку с печатью.interface Cat { meow(): void; } interface Dog { bark(): void; } // Смотри, магия: функция говорит — «Если я верну true, то pet — это 100% Cat, ёпта». function isCat(pet: Cat | Dog): pet is Cat { return (pet as Cat).meow !== undefined; // Или можно так: return 'meow' in pet; } function playWithPet(pet: Cat | Dog) { if (isCat(pet)) { pet.meow(); // Ок, это кот, можно мурякать // pet.bark(); // Ошибка компиляции! У кота такого метода нет, хитрая жопа! } else { pet.bark(); // TypeScript сам догадался, что если не кот — то собака } }
А нахуя это всё в Node.js? Да везде, блядь! Парсишь JSON от какого-нибудь кривого API — там unknown или any. Валидируешь тело HTTP-запроса — опять union-типы. Обрабатываешь ошибки, которые могут быть и системными, и твоими кастомными. Без type guards ты будешь постоянно делать as SomeType, а это прямой путь в ад, где в рантайме всё падает с Cannot read property 'meow' of undefined. Так что это не прихоть, а вопрос выживания в диком мире асинхронности и внешних данных.