Дженерики
Типы с параметрами, ограничения через extends, keyof и T[K].
Обновлено
Что такое дженерики
Дженерики (generics) — это типы с параметрами. Как функция принимает значения в параметрах, так дженерик принимает типы.
Какую проблему решают:
function first(arr: any[]): any { return arr[0] }
const n = first([1, 2]) // any — потеряна информация, что это number
function first<T>(arr: T[]): T | undefined { return arr[0] }
const m = first([1, 2]) // number ✓
const s = first(['a']) // string ✓T — параметр типа, пустое место, которое TS автоматически заполняет по аргументам. Функция остаётся одной, но тип результата следует за типом входа.
Вы их уже используете
const ids: Array<number> = [] // = number[]
const users = new Map<string, User>()
const [count, setCount] = useState<number>(0)
const res: Promise<User> = getUser()Ограничения через extends
Иногда T не может быть чем угодно — у него должно быть что-то определённое:
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b
}
longest('abc', 'de') // ✓ у строки есть length
longest([1, 2], [3]) // ✓ у массива есть length
longest(10, 20) // ✗ у number нет lengthkeyof и доступ по ключу
function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
return items.map(item => item[key])
}
pluck(users, 'email') // string[]
pluck(users, 'emial') // ✗ опечатка пойманаkeyof T = union ключей T; T[K] = тип значения по ключу K.
Обобщённые типы
Обобщёнными бывают не только функции:
type ApiResponse<T> =
| { ok: true; data: T }
| { ok: false; error: string }
type Paginated<T> = { items: T[]; page: number; total: number }
const res: ApiResponse<Paginated<Product>> = await api('/products')Когда нужны дженерики
| Ситуация | Дженерик? |
|---|---|
| функция, которая одинаково работает с любым типом (first, groupBy, pluck) | да |
| тип-«контейнер» (ответ API, пагинация, состояние формы) | да |
переиспользуемый React-компонент для любого типа элементов (List<T>, Select<T>) |
да |
функция, которая работает только с User |
нет — пишите User |
Признак перебора: T встречается в сигнатуре только один раз. Тогда дженерик не нужен.
Коротко
- Дженерик = тип с параметром; сохраняет связь между типом входа и типом выхода.
extendsзадаёт, что должно быть уT;keyof T+T[K]— для доступа по ключу.- Используйте для утилит и контейнеров, а не везде.