Zruck zum Blog, du Zniachtl
Jän. 2026·3 Min. zum LesnVo da KI gmocht

TypeScript Generics vastehn

#typescript#javascript

Generics san des Feature, über des Leit stolpern, grod nachdem's mit den einfachen TypeScript-Typen vertraut worn san - und dann, sobois's klickt, zu oam der meistgenutzten Werkzeuge in da Kistn wird. Die Kernidee is klan: Statt a Funktion oda an Typ für oan bestimmten Typ zum schreibn, schreibst ihn für "wöchan Typ a immer übergebn wird", und TypeScript verfolgt den Typ für dich durch den Rest vo da Signatur.

Des Problem, des Generics lösn

Sogn ma, du wüst a Funktion, de s'erste Element von an Array zruckgibt.

function firstOfNumbers(arr: number[]): number {
  return arr[0];
}

function firstOfStrings(arr: string[]): string {
  return arr[0];
}

Zwoa Funktionen, identische Logik, verschiedene Typen. Du kunntst's mit any typisieren:

function first(arr: any[]): any {
  return arr[0];
}

Des funktioniert, aber du host olle Informationen weggeschmissn. first([1, 2, 3]) hot jetzt Typ any statt number, drum fängt nix weiter unten an Fehler wia first([1, 2, 3]).toUpperCase() ab.

Generics erhoitn die Verbindung zwischen Input- und Output-Typ:

function first<T>(arr: T[]): T {
  return arr[0];
}

const n = first([1, 2, 3]);       // T inferred as number, n: number
const s = first(['a', 'b', 'c']); // T inferred as string, s: string

T is a Typparameter - a Platzhalter, den TypeScript auf Basis dessen befüllt, wos du tatsächlich übergibst, genauso wia a Funktionsparameter mit am Wert befüllt wird. Du hast ned first<number>([1, 2, 3]) schreibn miassn; TypeScript leitet T aus dem Argument ab.

Einschränkn, wos T sei kann

A uneingschränktes T kann olles sei, des heißt, du kannst koane bestimmte Form annehmen. Des kompiliert ned:

function getLength<T>(item: T): number {
  return item.length; // Error: Property 'length' does not exist on type 'T'
}

TypeScript beschwert si zu Recht - T kunnt number sei, und der hot ka .length. Schränk's mit extends ein:

function getLength<T extends { length: number }>(item: T): number {
  return item.length; // OK — T is guaranteed to have a `length` property
}

getLength('hello');      // OK, strings have .length
getLength([1, 2, 3]);    // OK, arrays have .length
getLength(42);           // Error: number doesn't satisfy the constraint

extends meint do ned Klassenvererbung - es heißt "T muass zu dera Form zuweisbar sei". Jeder Typ mit ana .length-Zahleneigenschaft erfüllt des.

Generische Interfaces und Klassen

Generics sand ned nur für Funktionen da - sie parametrisieren Typen söba. A Box<T>, de jeden einzelnen Wert hoitn kann:

interface Box<T> {
  value: T;
}

const numberBox: Box<number> = { value: 42 };
const stringBox: Box<string> = { value: 'hello' };

A realistischeres Beispiel - a typisierter API-Response-Wrapper, der in ana ganzen App genutzt wird:

interface ApiResponse<T> {
  data: T;
  status: number;
  error: string | null;
}

async function fetchUser(id: string): Promise<ApiResponse<User>> {
  const res = await fetch(`/api/users/${id}`);
  const data = await res.json();
  return { data, status: res.status, error: null };
}

Jeder Aufrufer von fetchUser kriagt response.data ois User typisiert, ned ois any - des Generic trägt den spezifischen Payload-Typ durch de gesamte Response-Form, ohne dass du für jede Entität a eigenes UserApiResponse, PostApiResponse und so weiter schreibn muasst.

Mehrere Typparameter

Generics kinnan mehr ois oan Parameter nehmen, was bei Funktionen üblich is, de zwoa unabhängige Dinge kombinieren - a Map-Funktion is des Paradebeispiel:

function mapArray<T, U>(arr: T[], fn: (item: T) => U): U[] {
  return arr.map(fn);
}

const lengths = mapArray(['a', 'bb', 'ccc'], (s) => s.length);
// T inferred as string, U inferred as number
// lengths: number[]

T is da Typ vom Eingabeelement, U is, wos da Callback zruckgibt - de miassn ned übereinstimmen, und TypeScript leitet beide unabhängig voneinander davon ab, wia du's aufrufst.

Standard-Typparameter

Wia Default-Funktionsparameter kinnan Generics Defaults hom, nützlich, wenn a Typparameter meistens des Söbe is, aber überschreibbar bleiben soi:

interface PaginatedResponse<T = unknown> {
  items: T[];
  page: number;
  totalPages: number;
}

function emptyPage(): PaginatedResponse {
  return { items: [], page: 1, totalPages: 1 }; // T defaults to unknown
}

function usersPage(): PaginatedResponse<User> {
  return { items: [], page: 1, totalPages: 1 }; // T explicitly User
}

A häufiger Fehler: Zu früh zu Generics greifn

Ned jede Funktion mit an Parameter muass generisch sei. Wenn a Funktion immer nur auf string operiert, fügt function process<T extends string>(input: T) an Typparameter dazu, der nie variiert und seine Komplexität nie rechtfertigt. Generics lohnen si, wenn die Beziehung zwischen Input- und Output-Typ über an Aufruf hinweg tatsächlich erhaltn bleibn muass - a first<T>, des den söbn Elementtyp zruckgibt, den's kriagt hot, a Box<T>, de beliebige Werte einpackt, a mapArray<T, U>, des an Typ in an ondern transformiert. Wenn si da Typ über Verwendungen hinweg nie ändert, is a konkreter Typ klarer ois a Generic, das niemand anders instanziiert. Merk da des, du Hirnederl.

Fazit

Generics gibt's, um a Beziehung zwischen Typen zu erhoitn, de any löschn würd und de N fast identische Overloads duplizieren würdn. Des mentale Modell, des's klicken lasst: a Typparameter is a Parameter, genauso wia a Funktionsargument, nur dass des, wos du übergibst, a Typ is, und TypeScript verfolgt ihn durch den Rest vo da Signatur wia an Wert.

NeicherFeature Flags: A praktischer LeitfodnElterAn LRU-Cache von Grund auf bauen