Ты ограничишь свои параметры типа через extends и задашь значения по умолчанию, чтобы строить удобные обобщённые API.
Открыть этот урок в KodokonДженерик без ограничений непрозрачен: внутри тела функции с T ничего нельзя сделать. Клауза extends задаёт минимальный контракт для параметра типа, что открывает доступ к гарантированным свойствам и при этом сохраняет точный тип вызывающей стороны. В этом принципиальная разница с параметром, типизированным интерфейсом: ограничение сохраняет информацию, интерфейс её стирает.
function longest<T extends { length: number }>(
a: T,
b: T
): T {
return a.length >= b.length ? a : b;
}
const word = longest("hello", "worldwide");
const list = longest([1, 2], [3, 4, 5]);Связка K extends keyof T - самый выгодный повседневный паттерн: она связывает два параметра типа между собой и статически гарантирует, что ключ действительно принадлежит объекту. Возвращаемый тип T[K] (тип с индексным доступом) точно отслеживает запрошенное свойство. Именно так типизированы функции вроде pick или pluck из утилитарных библиотек.
function getProp<T extends object, K extends keyof T>(
obj: T,
key: K
): T[K] {
return obj[key];
}
const config = { port: 8080, host: "localhost" };
const port = getProp(config, "port");
const host = getProp(config, "host");Значения по умолчанию (T = string) делают дженерик необязательным на месте вызова: аргумент типа указывают только тогда, когда хотят отойти от типичного случая. В сочетании с ограничением (T extends X = Y) они задают и границу, и неявное поведение. Значение по умолчанию должно удовлетворять ограничению, иначе не скомпилируется само объявление. Это стандартный паттерн для HTTP-клиентов, хранилищ и типизированных источников событий.
interface ApiResponse<T = unknown> {
status: number;
data: T;
}
async function fetchJson<T = unknown>(
url: string
): Promise<ApiResponse<T>> {
const res = await fetch(url);
return { status: res.status, data: await res.json() };
}
interface Post { id: number; title: string }
const typed = await fetchJson<Post>("/api/posts/1");
const loose = await fetchJson("/api/health");K extends keyof T в обобщённой сигнатуре?longest<T extends { length: number }> по сравнению с longest(a: { length: number }, b: ...) без дженериков?<T = unknown>, чем <T = any>?