Что такое type guards с помощью предикатов is в TypeScript?

MiddleTypeScript · Frontend·Обновлено 2 сентября 2026
Коротко
Это функции-предикаты вида 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-данных)

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее