Fortgeschritten
Generics in TypeScript: Wiederverwendbare Typen verstehen
Lerne, wie generische Typen in TypeScript funktionieren, wann du sie einsetzt und wie du damit flexiblen, typsicheren Code schreibst.
Inhalt
Generics in TypeScript: Wiederverwendbare Typen verstehen
Generics sind eines der maechtigsten Werkzeuge in TypeScript. Sie erlauben es, Funktionen, Klassen und Interfaces zu schreiben, die mit verschiedenen Datentypen arbeiten, ohne die Typsicherheit zu verlieren. Wer dieses Konzept einmal verstanden hat, schreibt deutlich weniger Duplikat-Code und gleichzeitig robustere Anwendungen.
Was sind Generics?
Ein Generic ist ein Platzhalter fuer einen Typ, der erst beim Aufruf der Funktion oder beim Erstellen einer Klasse festgelegt wird. Du kannst dir Generics wie Parameter vorstellen, aber fuer Typen statt fuer Werte.
Ohne Generics muesste man fuer jede Kombination aus Funktion und Datentyp eine eigene Implementierung schreiben. Mit Generics schreibst du einmal und verwendest es beliebig oft.
Die Grundsyntax
Ein Generic wird in spitzen Klammern angegeben. Traditionell nimmt man den Buchstaben T als Namen, aber jeder gueltiger Bezeichner funktioniert:
function identitaet<T>(wert: T): T {
return wert;
}
const zahl = identitaet<number>(42);
const text = identitaet<string>("Hallo");
TypeScript kann den Typparameter in vielen Faellen auch selbst ableiten:
const zahl = identitaet(42); // TypeScript leitet T = number ab
const text = identitaet("Hallo"); // TypeScript leitet T = string ab
Du kannst das Playground verwenden, um diese Typinferenz direkt im Browser auszuprobieren und die abgeleiteten Typen zu beobachten.
Generische Arrays und Rueckgabetypen
Ein haeufiger Anwendungsfall sind Funktionen, die mit Arrays arbeiten:
function erstesElement<T>(arr: T[]): T | undefined {
return arr[0];
}
const namen = ["Anna", "Bert", "Clara"];
const erster = erstesElement(namen); // Typ: string | undefined
const zahlen = [10, 20, 30];
const erstZahl = erstesElement(zahlen); // Typ: number | undefined
Constraints: Generics einschraenken
Manchmal soll ein Generic nicht jeden beliebigen Typ akzeptieren, sondern nur solche, die bestimmte Eigenschaften haben. Das erreicht man mit dem Schluesselwort extends:
interface HatLaenge {
length: number;
}
function laengePlusEins<T extends HatLaenge>(wert: T): number {
return wert.length + 1;
}
laengePlusEins("Hallo"); // 6
laengePlusEins([1, 2, 3]); // 4
// laengePlusEins(42); // Fehler: number hat keine length-Eigenschaft
Durch T extends HatLaenge stellt TypeScript sicher, dass nur Typen mit einer length-Eigenschaft uebergeben werden. Das ist praeziser als any und sicherer als ein allgemeines T.
Mehrere Typparameter
Du kannst mehrere Typparameter kombinieren. Ein klassisches Beispiel ist eine Funktion, die zwei Werte zu einem Paar zusammenfasst:
function paaren<K, V>(schluessel: K, wert: V): [K, V] {
return [schluessel, wert];
}
const eintrag = paaren("id", 123);
// Typ: [string, number]
const flagge = paaren("aktiv", true);
// Typ: [string, boolean]
Generische Interfaces und Klassen
Generics sind nicht nur auf Funktionen beschraenkt. Auch Interfaces und Klassen koennen Typparameter haben:
interface Behaelter<T> {
inhalt: T;
leer(): boolean;
}
class Box<T> implements Behaelter<T> {
constructor(public inhalt: T) {}
leer(): boolean {
return false;
}
}
const zahlenBox = new Box<number>(42);
const textBox = new Box<string>("TypeScript");
Generische Klassen sind die Grundlage fuer Datenstrukturen wie Listen, Stacks oder Queues, bei denen der Typ des gespeicherten Elements offen gelassen werden soll.
Default-Typen bei Generics
Seit TypeScript 2.3 koennen Generics einen Standardtyp haben, der verwendet wird, wenn kein expliziter Typ angegeben wird:
interface Antwort<T = string> {
daten: T;
statusCode: number;
}
const textAntwort: Antwort = {
daten: "OK",
statusCode: 200,
};
const zahlenAntwort: Antwort<number> = {
daten: 404,
statusCode: 404,
};
Das macht APIs einfacher zu verwenden, wenn ein sinnvoller Standardtyp existiert.
keyof und Lookup-Typen in Generics
Eine besonders nuetzliche Kombination ist keyof zusammen mit Generics. So kannst du Funktionen schreiben, die nur auf vorhandene Eigenschaften eines Objekts zugreifen duerfen:
function eigenschaft<T, K extends keyof T>(obj: T, schluessel: K): T[K] {
return obj[schluessel];
}
const person = { name: "Maria", alter: 30 };
const name = eigenschaft(person, "name"); // Typ: string
const alter = eigenschaft(person, "alter"); // Typ: number
// eigenschaft(person, "adresse"); // Fehler: 'adresse' existiert nicht
Diese Technik verhindert Tippfehler bei Eigenschaftsnamen zur Kompilierzeit.
Utility-Typen als Generics
TypeScript bringt viele eingebaute Utility-Typen mit, die intern alle auf Generics basieren. Dazu gehoeren Partial, Required, Readonly, Pick und Omit:
interface Nutzer {
id: number;
name: string;
email: string;
}
// Alle Eigenschaften optional machen
type TeilNutzer = Partial<Nutzer>;
// Nur bestimmte Felder auswaehlen
type NutzerKopf = Pick<Nutzer, "id" | "name">;
// Ein Feld entfernen
type OhneEmail = Omit<Nutzer, "email">;
Das Verstehen dieser Utility-Typen setzt grundlegendes Verstaendnis von Generics voraus und ist ein wichtiger Schritt auf dem Weg zum fortgeschrittenen TypeScript-Entwickler.
Tipps fuer den Alltag
Verwende aussagekraeftige Namen fuer Typparameter, wenn der Kontext komplex wird. Statt T macht TElement, TSchluessel oder TWert den Code lesbarer. Kurze Namen wie T und U sind in Ordnung fuer einfache Hilfsfunktionen, werden aber schnell unuebersichtlich.
Probiere Generics aktiv im Playground aus. Dort siehst du sofort, wie TypeScript die Typen ableitet und wo Einschraenkungen greifen. Das beschleunigt das Lernen erheblich.
Generics sind kein optionales Extra, sondern das Fundament vieler TypeScript-Bibliotheken. Wer sie beherrscht, kann mit Frameworks wie React, Angular oder NestJS deutlich effizienter arbeiten und eigene, robuste Abstraktionen bauen.
Häufige Fragen
Wann sollte ich Generics verwenden?
Immer dann, wenn eine Funktion oder Klasse mit verschiedenen Datentypen arbeiten soll, ohne dabei die Typsicherheit aufzugeben. Klassische Beispiele sind Hilfsfunktionen, Collections und API-Wrapper.
Was ist der Unterschied zwischen einem Generic und dem Typ 'any'?
Mit 'any' verlierst du alle Typinformationen und TypeScript kann keine Fehler mehr erkennen. Ein Generic behält die Typinformation und gibt sie weiter, sodass TypeScript Fehler zur Kompilierzeit finden kann.
Kann ich mehrere Typparameter in einem Generic verwenden?
Ja, du kannst beliebig viele Typparameter angeben, zum Beispiel in einer Funktion, die zwei verschiedene Typen kombiniert. Die Namen T, U, K, V sind Konvention, aber frei waehlbar.
Quellen
Über die Autorenschaft
Mateusz Viola
Betreiber und redaktionelle Verantwortung typescript-playground.de
Themengebiet: Mathematik, Kalenderrechnung, Schaltjahre, Statistik und ISO 8601
Mehr über Mateusz Viola →Verwandte Artikel
Grundlagen
Was ist TypeScript? Eine Einführung in die typisierte JavaScript-Erweiterung
TypeScript erweitert JavaScript um statische Typen und macht große Codebases wartbarer. Lerne die Grundlagen, die Geschichte und die wichtigsten Vorteile von TypeScript.
Lesezeit 7 Min.
Grundlagen
TypeScript vs. JavaScript: Die wichtigsten Unterschiede im Überblick
TypeScript und JavaScript unterscheiden sich grundlegend in der Typprüfung, Werkzeugunterstützung und Wartbarkeit. Dieser Ratgeber erklärt die Unterschiede und hilft bei der Wahl der richtigen Sprache.
Lesezeit 8 Min.
Typen
TypeScript Typen Grundlagen: string, number, boolean und mehr
Die primitiven Typen string, number und boolean sind das Fundament von TypeScript. Dieser Ratgeber erklärt alle grundlegenden Typen mit praktischen Beispielen.
Lesezeit 9 Min.