Что делает extends в TypeScript и зачем он нужен?
Что проверяют
Знаете ли вы, что у extends в TypeScript несколько ролей и в каждой он значит разное. Наследование классов знают все, поэтому следом почти всегда спрашивают про ограничения дженериков и условные типы.
Как отвечать
Разложите ответ по контекстам:
- класс:
class Dog extends Animalсоздаёт настоящее наследование через цепочку прототипов, оно остаётся в собранном JavaScript. В конструкторе наследникаsuper()обязан стоять до первого обращения кthis; - интерфейс:
interface Admin extends User, Auditableкопирует члены нескольких типов и проверяет их совместимость. Если свойство у предков конфликтует, будет ошибка компиляции. Всё это стирается при сборке; - ограничение дженерика:
function getProp<T, K extends keyof T>(obj: T, key: K): T[K]. Здесьextendsозначает «должен быть совместим с», а не «наследуется от»: подT extends stringподойдёт и литерал"ok"; - условный тип:
T extends U ? X : Yвместе сinferпозволяет вытащить тип из другого, какReturnTypeилиAwaited. ЕслиTэто голый параметр, условие распределяется по объединению:ToArray<string | number>даётstring[] | number[]. Отключается обёрткой[T] extends [U].
Хорошо дополнить сравнением с implements: он лишь проверяет, что класс соответствует контракту, ничего не наследуя. И сравнением с пересечением &: interface B extends A сразу покажет конфликт свойств, а type B = A & {...} молча превратит конфликтующее поле в never.
Для фронтенда полезно упомянуть, что extends есть и в tsconfig.json: общий базовый конфиг в монорепозитории подключают через "extends": "./tsconfig.base.json".
Где спотыкаются
Читают T extends X в дженерике как наследование и не могут объяснить, почему туда проходит обычный объект, никак не связанный с X. Типизация в TypeScript структурная: важна форма, а не родословная.
Не знают про дистрибутивность условных типов и удивляются, откуда в результате взялось объединение.
Наследуют Error при target: "ES5" и получают сломанный instanceof. Лечится Object.setPrototypeOf(this, new.target.prototype) в конструкторе или целевой версией ES2015 и выше.