> Можно ли создать второй интерфейс на основе первого, включающий все его поля, и как это сделать в TypeScript? (TypeScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: The Tribe

Стек: TypeScript

> Пример ответа

Короткий ответ

Да, можно. В TypeScript для этого используется ключевое слово extends в интерфейсах: interface Second extends First { ... }. Это включает все поля из First в Second, позволяя добавить новые или переопределить существующие (с совместимыми типами). Альтернативно можно использовать intersection type: type Second = First & { ... }. Оба подхода создают второй интерфейс на основе первого.

Подробное объяснение

TypeScript поддерживает наследование интерфейсов через extends. Когда один интерфейс расширяет другой, он автоматически получает все его поля, методы и индексы. Это позволяет строить иерархии типов без дублирования кода. При наследовании можно:

  • Добавлять новые поля
  • Переопределять типы существующих полей (с ограничением совместимости - новый тип должен быть подтипом исходного)
  • Расширять несколько интерфейсов одновременно: interface C extends A, B

Важно: при множественном наследовании поля с одинаковыми именами должны иметь идентичные типы, иначе возникнет ошибка компиляции.

Intersection type (&) работает похоже, но с ключевым отличием: он не создаёт новый именованный тип, а комбинирует существующие. При коллизии имён intersection type не выдаёт ошибку, а создаёт тип never для конфликтующего поля, что может привести к неожиданным проблемам.

На практике

Выбор между extends и & зависит от сценария:

  • extends предпочтителен для иерархий доменных моделей, где важна читаемость и явное именование
  • & удобен для временных комбинаций типов, утилит или когда нужно смешать несколько независимых интерфейсов

Для переопределения полей в extends нужно убедиться, что новый тип совместим с исходным. Например, можно сузить тип с string на 'a' | 'b', но нельзя расширить с string на string | number.

Пример кода

TYPESCRIPT
// Базовый интерфейс
interface User {
id: number;
name: string;
email: string;
}
// Наследование через extends
interface AdminUser extends User {
role: 'admin';
permissions: string[];
// Можно переопределить с сужением типа
name: `${string} (admin)`;
}
// Альтернатива через intersection type
type ModeratorUser = User & {
role: 'moderator';
moderatedSections: string[];
};
// Множественное наследование
interface Timestamped {
createdAt: Date;
updatedAt: Date;
}
interface AuditableUser extends User, Timestamped {
lastLogin: Date;
}
// Использование
const admin: AdminUser = {
id: 1,
name: 'Alice (admin)',
email: 'alice@example.com',
role: 'admin',
permissions: ['read', 'write']
};

Как отвечать на собеседовании

Начни с прямого ответа: да, через extends. Затем объясни разницу между extends и &, упомяни ограничения (совместимость типов при переопределении, коллизии). Приведи пример из реального проекта, где наследование интерфейсов помогло избежать дублирования. Если спросят про альтернативы, упомяни utility types вроде Pick, Omit, Partial для более тонкого контроля.

Что проверяет интервьюер

  • Понимание механизма наследования интерфейсов в TypeScript
  • Знание различий между extends и intersection types
  • Умение работать с иерархиями типов и избегать дублирования
  • Понимание ограничений при переопределении полей (ковариантность/контравариантность)
  • Способность выбрать правильный инструмент под задачу

Типичные ошибки

  • Попытка расширить тип (type alias) через extends - это работает только для интерфейсов
  • Игнорирование коллизий имён при множественном наследовании
  • Переопределение поля с несовместимым типом (например, string на number)
  • Использование & там, где логичнее extends, что ухудшает читаемость кода
  • Забывание, что extends работает и для классов, но с другими правилами

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью