Generics: продвинуто
keyof
Возвращает union ключей типа:
type User = { name: string; age: number };
type UserKeys = keyof User; // 'name' | 'age'
Практика:
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: 'Алиса', age: 25 };
const name = getValue(user, 'name'); // string
const age = getValue(user, 'age'); // number
getValue(user, 'email'); // ошибка
T[K] — тип значения по ключу.
typeof
Возвращает тип значения:
const user = { name: 'Алиса', age: 25 };
type User = typeof user;
// { name: string; age: number }
Ограничение extends
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
Параметр по умолчанию
type ApiResponse<T = unknown> = {
data: T;
status: number;
};
Несколько ограничений
function merge<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}
Дженерик с массивом
function groupBy<T, K extends keyof T>(items: T[], key: K): Record<string, T[]> {
return items.reduce((acc, item) => {
const group = String(item[key]);
acc[group] = acc[group] ?? [];
acc[group].push(item);
return acc;
}, {} as Record<string, T[]>);
}
Вложенные дженерики
type Container<T> = {
items: T[];
};
type Nested = Container<Container<string>>;
Распространение
function map<T, U>(items: T[], fn: (item: T) => U): U[] {
return items.map(fn);
}
map([1, 2, 3], (n) => n.toString()); // string[]
Generic constraints с keyof
function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
return items.map((item) => item[key]);
}
const users = [
{ name: 'Алиса', age: 25 },
{ name: 'Боб', age: 30 },
];
pluck(users, 'name'); // string[]
pluck(users, 'age'); // number[]
Conditional types
Синтаксис T extends U ? X : Y:
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
Infer
Извлекает тип из другого типа:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type A = ReturnType<() => string>; // string
Distributive conditional types
Условные типы распространяются на union:
type NonNullable<T> = T extends null | undefined ? never : T;
type A = NonNullable<string | null>; // string
Итоги
keyof— union ключейtypeof— тип значенияT[K]— тип значения по ключуextends— ограничение- Условные типы —
T extends U ? X : Y infer— извлечение типа