Что такое Type Guard в TypeScript?

«Что такое Type Guard в TypeScript?» — вопрос из категории TypeScript, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Type Guard (защита типа) в TypeScript — это выражение, которое выполняет проверку во время выполнения (runtime) и позволяет компилятору сузить тип переменной внутри определенной области видимости (например, внутри блока if). Это ключевой механизм для безопасной работы с union-типами, unknown или any.

Основные виды Type Guards:

  1. typeof Guard: Для примитивных типов.

    function processValue(value: string | number) {
      if (typeof value === 'string') {
        // Здесь TypeScript знает, что value — строка
        console.log(value.toUpperCase());
      } else {
        // Здесь value — число
        console.log(value.toFixed(2));
      }
    }
  2. instanceof Guard: Для проверки принадлежности к классу.

    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}`);
      }
    }
  3. Пользовательский 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.