type vs interface
Топ-5 вопрос на интервью. Многие отвечают «interface для объектов, type для всего остального» — это поверхностно. Реальная разница: declaration merging, computed keys, расширение примитивов/юнионов, скорость компилятора и качество сообщений об ошибках. Красный флаг — не знать про declaration merging.
Сравнение по ключевым осям:
| Свойство | type | interface |
|---|---|---|
| Declaration merging | ❌ Дублирование = ошибка | ✅ Объявления сливаются |
| Computed/mapped keys | ✅ { [K in Keys]: T } | ❌ Только литералы |
| Примитивы / union / tuple | ✅ type Id = string | number | ❌ Только object shapes |
| Синтаксис расширения | type B = A & { x: T } | interface B extends A { x: T } |
| Производительность / ошибки | Может «расклеиваться» в длинные intersection | Кэшируется, ошибки читабельнее |
| Implements у классов | ✅ если это shape | ✅ |
Declaration merging — главная фича interface: критично для расширения lib.d.ts и сторонних API.
// Declaration merging работает у interfaceinterface Window { myAnalytics: { track(e: string): void };}window.myAnalytics.track('login'); // ок
// type — нельзя дублироватьtype User = { id: string };// type User = { name: string }; // Error: Duplicate identifier 'User'.Computed / mapped keys и юниони — только через type:
type Keys = 'create' | 'read' | 'update' | 'delete';
// mapped type — ТОЛЬКО через typetype Permissions = { [K in Keys]: boolean };
// union / tuple / primitive aliastype Id = string | number;type Pair<A, B> = readonly [A, B];
// extends-style: для type — пересечениеtype Animal = { name: string };type Dog = Animal & { breed: string };Практическое правило: для публичных API библиотек используй interface (из-за merging и понятных ошибок). Для маппингов, юнионов и tuple — type.
Итог: interface — для расширяемых object shapes; type — для всего, что не object: union, tuple, primitive alias, mapped types.