Du schränkst deine Typparameter mit extends ein und gibst Standardwerte vor, um ergonomische generische APIs zu bauen.
Diese Lektion in Kodokon öffnenEin Generic ohne Constraint ist undurchsichtig: Im Rumpf der Funktion erlaubt dir T gar nichts. Die extends-Klausel legt einen Mindestvertrag für den Typparameter fest, was den Zugriff auf die garantierten Eigenschaften freischaltet und zugleich den präzisen Typ des Aufrufers bewahrt. Genau darin liegt der grundlegende Unterschied zu einem Parameter, der mit einem Interface typisiert ist: Das Constraint bewahrt die Information, das Interface löscht sie.
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]);Das Duo K extends keyof T ist das im Alltag lohnendste Muster: Es verknüpft zwei Typparameter miteinander und garantiert statisch, dass ein Schlüssel wirklich zum Objekt gehört. Der Rückgabetyp T[K] (ein indizierter Zugriffstyp) folgt der angefragten Eigenschaft ganz genau. Exakt so werden Funktionen wie pick oder pluck aus Utility-Bibliotheken typisiert.
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");Standardwerte (T = string) machen ein Generic an der Aufrufstelle optional: Nutzer geben das Typargument nur an, wenn sie vom Normalfall abweichen wollen. Kombiniert mit einem Constraint (T extends X = Y) legen sie sowohl die Schranke als auch das implizite Verhalten fest. Der Standardwert muss das Constraint erfüllen, sonst kompiliert schon die Deklaration selbst nicht. Das ist das Standardmuster für HTTP-Clients, Stores und typisierte Event-Emitter.
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 in einer generischen Signatur?longest<T extends { length: number }> gegenüber longest(a: { length: number }, b: ...) ohne Generics?<T = unknown> als Standardwert besser als <T = any>?