Что такое generics в TypeScript и как они помогают создавать переиспользуемые компоненты?
Что проверяют
Понимаете ли вы, что это связывание типов, а не способ обойти проверку.
Как отвечать
Обобщения позволяют написать код, работающий с разными типами, сохранив связь между ними. Именно связь отличает их от универсального типа, который проверку просто отключает.
function first<T>(items: T[]): T | undefined {
return items[0];
}
const n = first([1, 2, 3]); // number | undefined
const s = first(["a", "b"]); // string | undefinedТип выводится из аргумента, указывать его явно обычно не нужно.
Ограничения сужают допустимые типы и дают доступ к их свойствам:
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}Для компонентов это даёт настоящую переиспользуемость с сохранением типов:
type ListProps<T> = {
items: T[];
getKey: (item: T) => string;
renderItem: (item: T) => React.ReactNode;
};
function List<T>({ items, getKey, renderItem }: ListProps<T>) {
return <ul>{items.map((i) => <li key={getKey(i)}>{renderItem(i)}</li>)}</ul>;
}Компонент работает с любым типом элементов, и при этом внутри функций отрисовки редактор подсказывает поля конкретного объекта. Без обобщений пришлось бы либо писать список под каждый тип, либо принимать любой тип и потерять подсказки.
Полезные дополнения: значения по умолчанию для параметра типа, несколько параметров со связью между ними (типовой пример это функция выбора поля, где второй параметр ограничен ключами первого), и условные типы с извлечением, на которых построены встроенные вспомогательные типы.
Практическое правило: если параметр типа встречается в сигнатуре только один раз, он, скорее всего, лишний и его можно заменить обычным типом.
Где спотыкаются
Используют универсальный тип вместо обобщения и теряют проверку.
Пишут параметры типа там, где они ничего не связывают.
Не ставят ограничение и не могут обратиться к свойствам.
Явно указывают тип везде, хотя он выводится.