Загрузка…
Загрузка…
TypeScript · middle · сложность 6
Generics — параметры типов: пишете функции и структуры, которые работают с разными типами, не теряя связь «вход → выход». Передали string[] в firstElement — компилятор знает, что вернётся string, а не any.
Без дженериков у «первого элемента массива» два плохих варианта: any (потерять безопасность) или дублировать функцию на каждый тип. Generics — третий путь:
<T> — параметр типа; почти всегда заполняется inference.function first<T>(arr: T[]): T обещает тот же тип, что и элементы массива.extends: <T extends { id: number }> — «любой тип, но с числовым id».K extends keyof T: K — только реальный ключ T, доступ object[key] безопасен, возврат — T[K].В React вы уже ими пользуетесь: useState<number>(0).
// Without generics the type is lost:
// function firstElement(arr: any[]): any -> the output is any
// With generics, the input -> output relationship is preserved
function firstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
const num = firstElement([10, 20, 30]); // number | undefined
const word = firstElement(["a", "b", "c"]); // string | undefined
// num.toUpperCase();
// Error: Property 'toUpperCase' does not exist on type 'number'.
// Constraint with extends: T must have a length
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
longest("hello", "goodbye"); // works: string has length
longest([1, 2], [1, 2, 3]); // works: arrays have length
// longest(10, 20);
// Error: Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.
// K extends keyof T: only accepts real keys of the object
function getProperty<T, K extends keyof T>(object: T, key: K): T[K] {
return object[key];
}
const user = { name: "Ana", age: 30 };
const name = getProperty(user, "name"); // string
const age = getProperty(user, "age"); // number
// getProperty(user, "email");
// Error: Argument of type '"email"' is not assignable to parameter of type '"name" | "age"'.Подменить дженерик на any «потому что работает со всем»:
// Compiles, but the output loses its type
function firstBad(arr: any[]): any {
return arr[0];
}
const x = firstBad([1, 2, 3]);
x.toUpperCase(); // compiles and blows up at runtime: x is any
// Fix: the generic preserves the type
function firstGood<T>(arr: T[]): T | undefined {
return arr[0];
}
const y = firstGood([1, 2, 3]); // number | undefinedРазница: any принимает всё и забывает тип; generic принимает всё и помнит.
Generics параметризуют типы и сохраняют связь между аргументами и результатом — это безопасная альтернатива any для переиспользуемого кода.