Generics Basics
База, без которой не получится написать переиспользуемый код. На интервью просят написать identity, pluck с K extends keyof T и объяснить, почему параметр инферится из аргумента, но не из возврата. Красный флаг — лепить generics там, где они не нужны (один параметр, использованный один раз).
Базовый generic + constraint:
// Простейшийfunction identity<T>(x: T): T { return x; }
// Constraint: T должен иметь lengthfunction longest<T extends { length: number }>(a: T, b: T): T { return a.length >= b.length ? a : b;}
const s = longest('aa', 'bbb'); // stringconst arr = longest([1, 2], [1]); // number[]keyof-constraint — классика для безопасного доступа по ключу:
function pluck<T, K extends keyof T>(obj: T, keys: K[]): T[K][] { return keys.map(k => obj[k]);}
const user = { id: 'u_1', name: 'Ada', age: 36 };const v = pluck(user, ['id', 'name']); // (string | number)[] (точнее union)// pluck(user, ['unknown']); // ошибка: 'unknown' не keyofDefaults и порядок параметров. Без default иногда нужно явно указать тип:
interface ApiResponse<TData = unknown, TError = Error> { data: TData | null; error: TError | null;}
const r1: ApiResponse<{ id: string }> = { data: { id: 'u_1' }, error: null };const r2: ApiResponse = { data: null, error: new Error('x') }; // unknown / Error
// Inference: из аргументов — да, из возврата — нетfunction box<T>(): { value: T | undefined } { return { value: undefined }; }const b = box<string>(); // нужно явно: TS не может вывести TИтог: Generic нужен, когда тип используется в двух и более позициях и должен сохранять связь между ними.