Что такое type guards с помощью предикатов is в TypeScript?
function fn(x: unknown): x is Type, которые на этапе выполнения проверяют тип значения, а компилятору TypeScript сообщают, что внутри соответствующей ветки кода переменная имеет более узкий тип — то есть сужают (narrow) тип автоматически.Что такое type guard с предикатом is
Type guard — функция, которая во время выполнения проверяет тип значения, а во время компиляции сообщает TypeScript, что внутри блока if переменная имеет более узкий (специфичный) тип. Предикат is — особый синтаксис возвращаемого типа функции: function fn(x: unknown): x is Type. Компилятор доверяет этому объявлению и сужает тип аргумента в тех местах кода, где функция вернула true.
Синтаксис
function isString(value: unknown): value is string {
return typeof value === 'string';
}
Если isString(x) вернула true, TypeScript считает, что x имеет тип string в этой ветке кода — без ручных приведений через as.
Зачем нужны, если есть typeof и instanceof
typeof и instanceof работают только с примитивами и классами. Для более сложных случаев — union-типов, проверки формы объекта, различения интерфейсов без общего рантайм-механизма — своя логика проверки нужна, но без is компилятор не умеет её понимать и не сужает тип автоматически, даже если проверка написана верно.
Отличие от discriminated unions
Если у типов есть общее дискриминирующее поле (kind: 'cat' | 'dog'), TypeScript сам сузит тип по switch/if без всяких predicate-функций. is-предикаты нужны, когда такого поля нет либо проверка сложнее одного сравнения (например, проверка структуры объекта, полученного из JSON).
Где применяются на практике
- различение union-типов без общего discriminant-поля;
- валидация
unknown-данных, пришедших из API илиJSON.parse; - фильтрация массивов через
.filter(isX)с сохранением суженного типа результата (arr.filter(isCat)вернётCat[], а не(Cat | Dog)[]); - guard-функции внутри generic-кода и библиотек.
Важные нюансы
- Предикат — это утверждение программиста; компилятор не проверяет, что тело функции действительно соответствует заявленному типу. Если логика написана неверно, возникает ложное чувство безопасности типов и рассинхрон с реальными данными в рантайме.
- Кроме predicate-функций (
x is Type), существуют assertion functions (asserts x is Type) — они не возвращаютboolean, а бросают исключение при несовпадении типа и сужают тип после вызова, а не только внутриif. - Type guard может быть методом класса и использовать
this is Typeдля сужения типаthis. - Такие функции особенно полезны при работе с
unknownвместоany, так как заставляют явно описать и протестировать логику проверки типа.
Пример с несколькими типами
interface Cat { meow(): void }
interface Dog { bark(): void }
function isCat(pet: Cat | Dog): pet is Cat {
return typeof (pet as Cat).meow === 'function';
}
function speak(pet: Cat | Dog) {
if (isCat(pet)) {
pet.meow(); // здесь TS знает, что pet — Cat
} else {
pet.bark(); // а здесь — Dog
}
}
Что хочет услышать интервьюер
Кандидат объясняет, что is-предикат — это способ сообщить компилятору о сужении типа, а не просто способ вернуть boolean
Понимание, что проверка в теле функции не валидируется компилятором и ответственность за корректность лежит на разработчике
Умение привести практический пример: фильтрация union-типа, работа с unknown/JSON, .filter() с сохранением типа
Знание отличия от discriminated unions и от assertion functions (asserts x is Type)
Понимание, зачем это нужно вместо typeof/instanceof в сложных случаях
Пример: Базовый предикат для примитива
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function printLength(value: unknown) {
if (isString(value)) {
// тут value имеет тип string
console.log(value.length);
}
}
Пример: Различение union-типов без discriminant-поля
interface Cat { meow(): void }
interface Dog { bark(): void }
function isCat(pet: Cat | Dog): pet is Cat {
return typeof (pet as Cat).meow === 'function';
}
function speak(pet: Cat | Dog) {
if (isCat(pet)) {
pet.meow();
} else {
pet.bark();
}
}
Пример: Фильтрация массива с сохранением типа
const items: (string | number)[] = ['a', 1, 'b', 2];
function isNumber(value: string | number): value is number {
return typeof value === 'number';
}
// numbers имеет тип number[], а не (string | number)[]
const numbers = items.filter(isNumber);
Типичные ошибки
Путают type guard с обычной функцией, возвращающей boolean, не понимая, что именно даёт синтаксис is
Считают, что TypeScript проверяет корректность логики внутри функции-предиката — на деле это просто утверждение программиста
Не знают про assertion functions (asserts x is Type) и путают их с обычными type guard
Забывают, что предикат должен описывать подтип исходного типа параметра, иначе сужение может быть некорректным или бессмысленным
Не приводят практических примеров использования (фильтрация массивов, валидация unknown-данных)


