Дженерики
Дженерик параметризует функцию, класс или тип переменной типа, поэтому один и тот же код работает с разными типами, сохраняя точный тип. Классический пример — identity<T>(x: T): T: она возвращает ровно тот тип, что получила, так что identity(5) имеет тип number, а не размытый any.
Именно этим дженерик отличается от any: any стирает тип и отключает проверки, а параметр типа сохраняет связь между входом и выходом, оставаясь полностью типобезопасным. Дальше эта идея масштабируется: ограничения через extends и keyof сужают то, что можно передать, не теряя точности, а встроенные utility-типы (Partial, Pick, Omit, Record) — это дженерики поверх mapped-типов, переиспользуемые преобразования формы. Полная карта — в слоях ниже.
Карта темы
- Дженерики — параметр типа
<T>, дженерик-функции, классы и интерфейсы; вывод типа и почему дженерик — неany. - Ограничения (extends) —
<T extends U>как верхняя граница,keyofи<K extends keyof T>для безопасной индексации. - Utility-типы —
Partial,Required,Pick,Omit,Record,Readonlyкак дженерики поверх mapped-типов.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Брать any вместо параметра типа | Теряется связь типа входа и выхода — компилятор больше ничего не проверяет |
| Думать, что дженерики существуют в рантайме | Как и все типы, они стираются при компиляции — рефлексии по T нет |
Ограничивать параметр через implements | Ограничение задаётся через extends; implements — это про классы, не про параметры типа |
Считать, что <T extends U> прикалывает T к U | extends задаёт верхнюю границу, а не точный тип — T может быть любым подтипом U |
Путать Pick и Omit | Pick<T, K> оставляет ключи K, Omit<T, K> их отбрасывает |
| Ждать от utility-типов работы в рантайме | Это операции над типами на этапе компиляции, а не функции — вызвать их нельзя |
Значение для собеседований
Дженерики — одна из самых частых тем на TypeScript-интервью, потому что за ними стоит понимание всей системы типов. Кандидат, который объясняет, что дженерик сохраняет тип там, где any его теряет, сразу показывает зрелость.
Что обычно проверяют:
- Чем дженерик отличается от
anyи почемуidentity(5)— этоnumber, а неany. - Может ли компилятор вывести аргумент типа или его нужно передавать явно.
- Как ограничить параметр через
extendsи что даёт<K extends keyof T>. - Как устроены
Partial,Pick,Omit,Recordи что они компилируются в ничто. - Разница
Pick/Omitи на чём работаетExclude(объединение, а не ключи объекта).
Типичный неверный ответ: «дженерик — это просто красивый any». Это открывает разговор о том, что any рвёт связь входа и выхода, а дженерик её держит, поэтому один типобезопасный код заменяет стопку перегрузок.