Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Type Guard в TypeScript
Type Guard — это механизм TypeScript, позволяющий сузить (narrow) тип переменной внутри определённого блока кода. Без Type Guard компилятор не знает конкретного типа переменной в runtime, и мы не можем безопасно обращаться к специфичным для этого типа свойствам или методам.
Зачем нужен Type Guard?
TypeScript работает на уровне статической типизации, но в runtime типы стираются. Когда у переменной тип string | number или Animal | Car, нельзя просто так вызвать метод, который есть только у одного из типов — компилятор выдаст ошибку. Type Guard решает эту проблему, давая компилятору понять: «внутри этого if-блока тип точно такой-то».
Виды Type Guard
1. typeof
Используется для примитивных типов (string, number, boolean, symbol, bigint, undefined, function).
function printValue(val: string | number) {
if (typeof val === "string") {
console.log(val.toUpperCase()); // val: string
} else {
console.log(val.toFixed(2)); // val: number
}
}
2. instanceof
Используется для классов и конструкторов.
class Dog { bark() { return "Woof!"; } }
class Cat { meow() { return "Meow!"; } }
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
console.log(animal.bark()); // animal: Dog
} else {
console.log(animal.meow()); // animal: Cat
}
}
3. in оператор
Проверяет наличие свойства в объекте. Удобен для union-типов с объектами.
interface Bird { fly(): void; }
interface Fish { swim(): void; }
function move(pet: Bird | Fish) {
if ("fly" in pet) {
pet.fly(); // pet: Bird
} else {
pet.swim(); // pet: Fish
}
}
4. Пользовательский (user-defined) Type Guard
Функция, возвращающая predicate value is Type. Самый гибкий вариант.
interface Car { make: string; speed: number; }
interface Boat { name: string; knots: number; }
function isCar(vehicle: Car | Boat): vehicle is Car {
return (vehicle as Car).speed !== undefined;
}
function describe(vehicle: Car | Boat) {
if (isCar(vehicle)) {
console.log(`Car: ${vehicle.make}, speed: ${vehicle.speed}`);
} else {
console.log(`Boat: ${vehicle.name}, knots: ${vehicle.knots}`);
}
}
5. Discriminated Union (теговые объединения)
Общий паттерн: добавить поле-дискриминатор (kind, type, tag) в каждый вариант union.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rect"; width: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rect":
return shape.width * shape.height;
}
}
Assertion functions
Ещё один вариант — функции-ассерции (TypeScript 3.7+), которые сужают тип через выброс исключения:
function assertIsString(val: unknown): asserts val is string {
if (typeof val !== "string") {
throw new Error(`Expected string, got ${typeof val}`);
}
}
function process(val: unknown) {
assertIsString(val);
console.log(val.toUpperCase()); // val: string
}
Практическое применение
- API responses: данные из
fetchприходят какunknown, нужно проверить структуру перед использованием. - Event handlers:
event.targetчасто типизирован какEventTarget, нужно сузить доHTMLInputElement. - Обработка ошибок:
catch (e)возвращаетunknown(TS 4+), необходимо проверить, что этоError.
try {
// ...
} catch (e) {
if (e instanceof Error) {
console.error(e.message);
}
}
Итог
Type Guard обеспечивает безопасное сужение типов без as-кастов (которые отключают проверки). Это позволяет писать код, который одновременно корректен с точки зрения типов и безопасен в runtime. Без Type Guard пришлось бы либо использовать небезопасные касты as, либо отказываться от union-типов вовсе.