Type Guards — это механизмы, которые позволяют уточнять тип переменной в блоке кода, основываясь на некоторых проверках (например, с помощью typeof, instanceof, либо специальных функций, возвращающих выражения вида param is SomeType).
Главное преимущество — это безопасность и исключение ошибок выполнения за счет проверки типов во время компиляции.
Пример:
interface Fish { swim: () => void } interface Bird { fly: () => void } function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } function move(pet: Fish | Bird) { if (isFish(pet)) { pet.swim(); } else { pet.fly(); } }
Здесь функция isFish — это пользовательский type guard.
Нюансы:
Вопрос: "Будет ли компилятор TypeScript всегда полагаться только на возвращаемое значение функции guard, или использует еще какой-то анализ внутри функции?"
Ответ:
Компилятор TypeScript опирается только на сигнатуру возвращаемого значения param is Type. То, что происходит внутри функции-guard, не анализируется на предмет корректности реализации.
Пример (опасная ошибка!):
function isString(x: any): x is string { return true; } // Компилятор будет считать, что всегда строка, хотя это не так: if (isString(123)) { // тут x типа string, но на самом деле это число }
История
На проекте с разделяемыми DTO между фронтом и бэкендом забыли добавить строгую проверку внутри пользовательского type guard. В результате, часть данных ошибочно воспринималась как нужный тип, приводя к сбоям на клиенте при попытке использовать отсутствующее свойство.
История
Разработчик написал type guard, полагаясь на опциональное поле, однако структура данных позволяла вовсе не иметь этого поля. В итоге типовому switch-case не доставало ветви, и компилятор предупреждения не выдавал — в рантайме возникали исключения.
История
В одном из сервисов при переходе на TypeScript полагались только на встроенные type guards (typeof, instanceof). При смене прототипа объектов во время выполнения проверки становились некорректными, что вызвало сложные к отладке баги на проде.