← Назад к вопросам

Что чаще использовал с TypeScript на последнем проекте, типы или интерфейсы?

2.2 Middle🔥 161 комментариев
#TypeScript

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Моя практика использования TypeScript: типы vs интерфейсы

На последних проектах я использовал как типы (type), так и интерфейсы (interface), но с четким разделением сценариев применения. Это не вопрос "или-или", а выбор подходящего инструмента для конкретной задачи, основанный на их семантических и технических различиях.

Случаи, где я предпочитал интерфейсы

Интерфейсы были основным выбором для описания публичных API, особенно при работе с объектами и классами, где важна декларация формы.

// Интерфейс для API модели пользователя
interface IUser {
  id: string;
  email: string;
  firstName: string;
  lastName: string;
  getFullName(): string;
}

// Интерфейс для контракта сервиса (удобно для DI)
interface IAuthService {
  login(credentials: LoginDto): Promise<AuthResponse>;
  logout(): void;
  isAuthenticated: boolean;
}

// Расширение интерфейсов (декларативное мерджинг)
interface IAdminUser extends IUser {
  permissions: string[];
  role: 'admin' | 'super-admin';
}

Преимущества интерфейсов в этих сценариях:

  • Четкая семантика: interface заявляет о контракте, о том, что объект должен реализовывать определенную структуру. Это особенно важно в ООП-стиле.
  • Декларативное слияние (declaration merging): Критично при работе со сторонними библиотеками или при расширении глобальных объектов (например, в window).
  • Лучшая ошибка в IDE: При реализации класса некорректно сообщения об ошибках часто более читаемы.
  • Производительность при проверке типов: В некоторых сценариях больших проектов interface может работать немного быстрее, хотя это решающий фактор только на огромных codebase.

Случаи, где я выбирал типы (type alias)

Типы были незаменимы для создания сложных составных типов, работы с примитивами и юнион-типами.

// Примитивный alias для семантики
type UUID = string;
type Email = string;

// Юнион- и литеральные типы (невозможно с interface)
type Status = 'idle' | 'loading' | 'success' | 'error';
type ButtonSize = 'sm' | 'md' | 'lg';
type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;

// Кортежи (tuples)
type Point2D = [number, number];
type HttpStatusCode = [number, string];

// Сложные маппированные типы (Mapped Types) и утилиты
type ReadonlyUser = Readonly<IUser>;
type UserKeys = keyof IUser;
type PartialUser = Partial<IUser>;
type PickUser = Pick<IUser, 'id' | 'email'>;

// Условные типы (Conditional Types) в утилитарных функциях
type Nullable<T> = T | null | undefined;
type ExtractAdmin<T> = T extends { role: 'admin' } ? T : never;

Преимущества типов в этих сценариях:

  • Гибкость: Возможность создавать типы из любых других типов с помощью операторов (|, &, keyof и т.д.).
  • Работа с примитивами: Можно дать семантическое имя (Email) простому string.
  • Юнион-типы: Фундаментальная возможность, отсутствующая у интерфейсов.
  • Кортежи: Лаконичное описание массивов фиксированной длины.

Ключевой принцип и итог

Мой основной принцип, сформированный практикой:

Используй interface для определения форм объектов и публичных контрактов, которые могут расширяться. Используй type для создания псевдонимов, композиций, юнионов и более абстрактных конструкций.

На проекте это выглядело так: интерфейсы доминировали в слоях models/, services/, components/ (для props в React, если они объекты), описывая сущности предметной области. Типы активно использовались в utils/types/, lib/, для стейта менеджеров (например, экшены в Redux Toolkit часто описываются через юнион-типы), типизации функций-хелперов и конфигураций.

Важный нюанс: С появлением возможностей пересечения (&) у типов и с развитием IDE разница стерлась. Во многих случаях выбор становится стилистическим. На проекте мы зафиксировали этот выбор в ESLint правиле (@typescript-eslint/consistent-type-definitions), установив его в значение "interface", чтобы поощрять единообразие там, где нет технических причин использовать type. Это предотвращает хаотичное смешение двух синтаксисов для одной и той же цели.