Einleitung: Was sind Generics und warum sind sie wichtig
TypeScript ist eine streng typisierte Sprache, die JavaScript ein leistungsstarkes Typsystem hinzufügt. Allerdings kann strenge Typisierung manchmal das Schreiben von flexiblem und wiederverwendbarem Code behindern. Genau hier kommen Generics (generische Typen) ins Spiel.
Generics sind ein Werkzeug, das es ermöglicht, Komponenten (Funktionen, Klassen, Interfaces) zu erstellen, die mit verschiedenen Datentypen arbeiten, ohne dabei die Typkontrolle zu verlieren. Stellen Sie sich vor, Sie schreiben eine Funktion, die sowohl mit Zahlen als auch mit Strings und Arrays arbeiten soll. Ohne Generics müssten Sie entweder any verwenden (was den Vorteil der Typisierung zunichte macht) oder separate Funktionen für jeden Datentyp erstellen. Generics lösen dieses Problem elegant und sicher.
In diesem Artikel werden wir untersuchen, wozu Generics in TypeScript dienen, und vor allem, wie man sie in der Praxis einsetzt – von einfachen Funktionen bis hin zu komplexen Klassen und Typeinschränkungen.
Hauptteil
1. Generics in Funktionen: Von der Theorie zur Praxis
Das einfachste und häufigste Beispiel für die Verwendung von Generics sind Funktionen, die Daten desselben Typs empfangen und zurückgeben. Betrachten wir eine klassische Aufgabe: eine Funktion, die das erste Element eines Arrays zurückgibt.
Schlechter Ansatz (ohne Generics):
// Beispiel mit any – wir verlieren die Typinformationfunction getFirst(arr: any[]): any { return arr[0];}
const num = getFirst([1, 2, 3]); // Typ von num ist any, wir wissen nicht, dass es eine Zahl istRichtiger Ansatz (mit Generics):
// Verwendung des generischen Typs Tfunction getFirst<T>(arr: T[]): T { return arr[0];}
const num = getFirst([1, 2, 3]); // Typ von num ist numberconst str = getFirst(['a', 'b', 'c']); // Typ von str ist stringconst bool = getFirst([true, false]); // Typ von bool ist booleanWie funktioniert das? Wir deklarieren einen Typparameter <T> (der Name kann beliebig sein, aber üblich sind T, U, V). TypeScript leitet automatisch den konkreten Typ basierend auf dem übergebenen Argument ab. Dies nennt man Typinferenz.
Sie können den Typ auch explizit beim Aufruf der Funktion angeben:
const num = getFirst<number>([1, 2, 3]); // explizite TypangabeEin weiteres nützliches Beispiel ist eine Funktion, die ein zufälliges Element eines Arrays zurückgibt:
function getRandom<T>(items: T[]): T { const randomIndex = Math.floor(Math.random() * items.length); return items[randomIndex];}
const randomNumber = getRandom([10, 20, 30]); // Typ ist numberconst randomName = getRandom(['Anna', 'Ivan', 'Maria']); // Typ ist string2. Generics in Interfaces und Typen
Generics funktionieren auch hervorragend mit Interfaces. Dies ermöglicht die Erstellung flexibler Datenstrukturen, die Werte verschiedener Typen speichern können.
Stellen Sie sich vor, wir möchten eine API-Antwort beschreiben, die Daten beliebigen Typs enthalten kann:
interface ApiResponse<T> { status: number; message: string; data: T;}
// Verwendung für verschiedene Datentypenconst userResponse: ApiResponse<{ id: number; name: string }> = { status: 200, message: 'Erfolgreich', data: { id: 1, name: 'Alexej' }};
const listResponse: ApiResponse<string[]> = { status: 200, message: 'Liste erhalten', data: ['Apfel', 'Banane', 'Orange']};Man kann Generics auch mit Typ-Aliasen (type) verwenden:
type Result<T> = { success: boolean; value: T; error?: string;};
const successResult: Result<number> = { success: true, value: 42 };const errorResult: Result<string> = { success: false, error: 'Ladefehler', value: '' };Beachten Sie: Wir können ko