ProgrammierungTypeScript Architekt

Wie funktionieren bedingte Typen (conditional types) in TypeScript? Was ist das distributive Verhalten von bedingten Typen und wie arbeitet man damit? Erklären Sie anhand von Beispielen.

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

Bedingte Typen (conditional types) in TypeScript ermöglichen es, einen Typ anhand eines anderen zu beschreiben nach dem Prinzip T extends U ? X : Y. Solche Typen bieten mächtige Möglichkeiten beim Aufbau komplexer Typlogik, insbesondere in Bibliotheken und API-Deklarationen.

Beispiel eines grundlegenden bedingten Typs:

type IsString<T> = T extends string ? 'yes' : 'no'; type A = IsString<string>; // 'yes' type B = IsString<number>; // 'no'

Distributives Verhalten

Wenn ein bedingter Typ auf einen Union-Typ angewendet wird, "verteilt" TypeScript die Bedingung auf jedes Element des Unions separat. Dies nennt man das distributive Verhalten von bedingten Typen.

Beispiel:

type Foo<T> = T extends { id: number } ? string : boolean; type Result = Foo<{id: number} | {name: string}>; // string | boolean

Dies ist eine sehr mächtige Funktion, kann jedoch zu Verwirrung über das erwartete Ergebnis führen, insbesondere beim Arbeiten mit Arrays und Typ-Mapping.

Wie man das distributive Verhalten vermeidet

Umwickeln des Typs in ein Tupel:

type NoDistrib<T> = [T] extends [{id: number}] ? string : boolean; type Result = NoDistrib<{id: number} | {name: string}>; // boolean

Fangfrage.

Frage: "Was passiert, wenn man einen bedingten Typ mit Union-Typen verwendet, ohne sie in Tupel zu umwickeln? Ist das Ergebnis immer das gleiche wie bei normaler Logik?"

Antwort: Das Ergebnis kann unerwartet sein! Aufgrund der Distributivität wird die Bedingung auf jedes Element der Union-Typen getrennt angewendet. Um den gesamten Union-Typ streng zu vergleichen, muss eine Umhüllung (Tupel) verwendet werden.

Beispiel:

type Test<T> = T extends string ? number : boolean; type A = Test<string | boolean>; // number | boolean, nicht nur boolean

Beispiele realer Fehler aufgrund von Unkenntnis der Feinheiten des Themas.


Geschichte

In einer Bibliothek zur Serialisierung wurde ein bedingter Typ zur Überprüfung der Datenstruktur verwendet, aber der generische Parameter wurde nicht in ein Tupel gewickelt. Infolgedessen brachen die Deklarationen bei komplexen Union-Typen, und der Compiler gab unvorhersehbare Typen bei der Verwendung der API aus.


Geschichte

Bei dem Versuch, eine Typumwandlung zur Bearbeitung von Feldern in Modellen zu implementieren, ging ein Teil der Informationen verloren: Da die Distributivität Vereinigungen erzeugt, wurden einige Zweige der Logik vergessen zu verarbeiten, was dazu führte, dass die Typisierung zu permissiv wurde.


Geschichte

Ein Entwickler war der Meinung, dass T extends SomeType innerhalb bedingter Typen sich so verhalten würde, wie wir es für das gesamte Objekt erwarten, aber es kam zur "Zersplitterung" — der Compiler wies auf inkonsistente Typen hin, was zu Typ-Chaos und schwerwiegenden Fehlern bei der automatischen Dokumentationserstellung führte.