TypeScriptMiddleTechnical
В чём разница между расширением одного interface другим и использованием intersection types?
extends проверяет совместимость конфликтующих полей на этапе объявления и поддерживает declaration merging; intersection тихо превращает конфликтующие поля в never и подходит для произвольной композиции типов.
Наследование интерфейса через extends
Когда один интерфейс расширяет другой, TypeScript требует, чтобы дочерний интерфейс был совместим с родительским. Одинаковые свойства должны иметь совместимые типы — несовместимое переопределение является ошибкой на этапе объявления:
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
// age: string; // ошибка: string не совместим с number
}
const d: Dog = { name: "Rex", age: 3, breed: "Husky" };
Intersection types (&)
Intersection создаёт новый тип, объединяющий все свойства нескольких типов. При конфликте типов одного поля результирующее поле получает тип never — без каких-либо ошибок на уровне объявления:
type A = { name: string; age: number };
type B = { name: string; age: string }; // конфликт age
type AB = A & B;
// AB.age имеет тип never (number & string)
const x: AB = {
name: "test",
age: 42 as never, // приходится обходить, что скрывает баг
};
Ключевые различия
- Проверка совместимости: extends проверяет конфликты сразу, intersection — нет. Конфликтующие поля тихо становятся
never. - Декларативное слияние: интерфейсы поддерживают declaration merging (два
interface Fooв разных файлах сливаются), type-алиасы — нет. - Применимость:
interface extendsработает только с интерфейсами и классами. Intersection работает с любыми типами, включая примитивы и объединения. - Читаемость в IDE: extends даёт чистую иерархию, которую легко читать в тултипах. Intersection разворачивается в плоский тип.
- Производительность компилятора: глубокие цепочки intersection могут замедлять type-checking на больших проектах.
Когда что использовать
// Предпочтительно для ООП-иерархий и публичных API
interface Shape {
color: string;
}
interface Circle extends Shape {
radius: number;
}
// Предпочтительно для композиции несвязанных типов
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type WithId = { id: string };
type Entity = WithId & WithTimestamps;
// Миксин-паттерн (только через intersection)
type Serializable = { serialize(): string };
type Loggable = { log(): void };
type Service = Entity & Serializable & Loggable;
Ещё один важный нюанс: методы vs call signatures
// extends: метод ковариантен
interface ReadonlyArray<T> {
map<U>(fn: (v: T) => U): U[];
}
// intersection с function types: тип вычисляется через overload
type Fn = ((x: string) => string) & ((x: number) => number);
// Fn — перегруженная функция
Подводные камни
- Конфликт типов поля при intersection даёт
neverбез предупреждения, что обнаруживается только при попытке присвоить значение. - Declaration merging интерфейсов может неожиданно сработать в монорепо, если два пакета объявляют одноимённый интерфейс в глобальном пространстве.
- Intersection глубоко вложенных объектов не делает глубокое слияние — вложенные объекты остаются отдельными типами.
- extends нельзя применить к union type (
interface A extends B | C— ошибка), intersection — можно. - При использовании intersection с
classв качестве типа теряется возможность проверкиinstanceof. - Сложные intersection замедляют автодополнение в VSCode на файлах с большим числом типов.
Common mistakes
- Смешивать «interface extends и intersection types» с похожим механизмом без критерия выбора.
- Игнорировать риск: неверно оценить границы применения темы «interface extends и intersection types» и получить хрупкое решение.
- Показывать только синтаксис и не объяснять поведение в runtime или сборке.
What the interviewer is testing
- Объясняет два способа композиции объектных типов с разным поведением конфликтов.
- Показывает на примере, как работает:
extendsявно наследует объектный контракт и понятнее для публичных interface, а intersection пересекает требования и может привести кneverпри несовместимых свойствах. - Называет production-нюанс и граничный случай для темы «interface extends и intersection types».