Проверка типов интерфейсов в TypeScript: частые вопросы и ответы

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему интерфейсы не работают во время выполнения?

Привет, друг! Если ты хоть раз писал на TypeScript, то наверняка сталкивался с ситуацией: ты создаёшь идеальный интерфейс, описываешь все поля, проверяешь типы на этапе компиляции — и вдруг осознаёшь, что в рантайме (runtime) твой код ведёт себя так, будто никаких интерфейсов не существует. Знакомо? 🎯

Это один из самых популярных вопросов на Stack Overflow: "Interface type check with TypeScript" — тысячи просмотров, сотни ответов. И неудивительно, ведь TypeScript стирает интерфейсы во время компиляции. В отличие от классов, интерфейсы — это чисто декларативная конструкция для разработчика и компилятора. В JavaScript их нет.

В этой статье мы разберёмся, как обойти это ограничение, научимся проверять соответствие объекта интерфейсу в рантайме и ответим на самые частые вопросы. Поехали! 🚀

Почему мы вообще хотим проверять интерфейсы?

Представь: ты получаешь данные от API, из формы или из WebSocket. Ты уверен, что они соответствуют интерфейсу User, но вдруг приходит объект без поля email или с неправильным типом. Если не проверить — твой код упадёт с ошибкой. Интерфейсы TypeScript проверяют только на этапе разработки, а не в продакшене. Значит, нужны runtime-проверки.

Проблема: интерфейсы не существуют в рантайме

В TypeScript интерфейсы — это "синтаксический сахар" для статической типизации. Когда ты компилируешь код, интерфейсы исчезают. Вот простой пример:

interface User {
  name: string;
  age: number;
}

const data: unknown = JSON.parse('{"name": "Алиса"}');
if (data instanceof User) { // ❌ Ошибка! User — это интерфейс, не класс
  console.log(data.name);
}

Компилятор скажет: 'User' only refers to a type, but is being used as a value here. Это главная ловушка для новичков. Но не волнуйся — есть несколько проверенных способов обойти это.

Способ 1: Пользовательские защитники типов (User-Defined Type Guards)

Самый популярный и элегантный способ — написать функцию-защитник типа. Она проверяет структуру объекта и возвращает true/false, а TypeScript "понимает", что внутри блока с true объект точно соответствует интерфейсу.

interface User {
  name: string;
  age: number;
  email?: string; // необязательное поле
}

function isUser(obj: unknown): obj is User {
  if (typeof obj !== 'object' || obj === null) return false;

  const user = obj as Record<string, unknown>;
  return (
    typeof user.name === 'string' &&
    typeof user.age === 'number' &&
    (user.email === undefined || typeof user.email === 'string')
  );
}

// Пример использования
const data: unknown = JSON.parse('{"name": "Боб", "age": 30}');

if (isUser(data)) {
  // Теперь TypeScript знает, что data — это User
  console.log(data.name.toUpperCase()); // ✅ Работает
}

Плюсы: полный контроль, типы корректно выводятся, легко расширять.
Минусы: нужно писать вручную для каждого интерфейса (но есть библиотеки!)

Что такое obj is User?

Это синтаксис защитника типа. Он говорит TypeScript: "Если функция вернёт true, то аргумент obj можно считать типом User". Это магия, которая делает runtime-проверку полезной для статического анализа.

Способ 2: Использование классов вместо интерфейсов

Классы — единственная конструкция TypeScript, которая сохраняется в JavaScript. Если тебе нужно проверять тип в рантайме, можно заменить интерфейс на класс с проверкой через instanceof.

class User {
  constructor(
    public name: string,
    public age: number,
    public email?: string
  ) {}
}

const data = new User('Чарли', 25);
if (data instanceof User) {
  console.log(data.name); // ✅ Чарли
}

Но есть нюанс: если данные приходят из JSON, они не будут экземплярами класса. Тебе придётся вручную создавать объект через конструктор. Зато instanceof работает надёжно.

Когда это удобно?

Если ты контролируешь создание объектов (например, через фабрику или ORM) — классы отлично подходят. Если данные приходят из внешнего источника — лучше использовать защитники типов.

Способ 3: Библиотеки для валидации (io-ts, zod, class-validator)

Писать защитники типов вручную для каждого интерфейса — утомительно. На помощь приходят библиотеки, которые генерируют runtime-валидаторы на основе схем.

Пример с Zod (самая популярная библиотека)

import { z } from 'zod';

const UserSchema = z.object({
  name: z.string(),
  age: z.number(),
  email: z.string().email().optional(),
});

type User = z.infer<typeof UserSchema>;

// Проверка
const data = JSON.parse('{"name": "Диана", "age": 28}');
const result = UserSchema.safeParse(data);

if (result.success) {
  const user: User = result.data; // ✅ Тип выведен
  console.log(user.name);
} else {
  console.error('Ошибка валидации:', result.error);
}

Плюсы: минимум кода, автоматическая генерация типов, подробные ошибки.
Минусы: дополнительная зависимость, небольшой overhead.

Альтернативы: io-ts, class-validator

io-ts — функциональный подход, популярен в сообществе FP.
class-validator — отлично работает с декораторами и NestJS.

Способ 4: Утилита hasOwnProperty и проверка ключей

Если тебе нужно проверить только наличие определённых полей (без проверки типов), можно использовать простой перебор:

interface User {
  name: string;
  age: number;
}

function hasUserKeys(obj: unknown): boolean {
  if (typeof obj !== 'object' || obj === null) return false;
  return 'name' in obj && 'age' in obj;
}

const data = { name: 'Ева', age: 22 };
if (hasUserKeys(data)) {
  // data всё ещё unknown, но мы знаем, что поля есть
  console.log((data as User).name);
}

Этот способ не проверяет типы значений, поэтому его лучше комбинировать с typeof или использовать как быстрое решение.

Частые вопросы (ответы на основе Stack Overflow)

Вопрос 1: Можно ли использовать typeof для проверки интерфейса?

Нет, typeof работает только с примитивами (string, number, boolean, object, undefined, function, symbol, big

Блоги

Рекомендаций по книгам