Conditional Types in TypeScript
Conditional Types
~15 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Das letzte große Werkzeug für ECHTE Typ-Logik: Conditional Types – "Wenn-Dann"-Entscheidungen, aber auf TYP-Ebene statt auf Werte-Ebene, ausgewertet zur Kompilierzeit.
Die Grundsyntax
type IstString<T> = T extends string ? true : false;
type A = IstString<'hallo'>; // true
type B = IstString<42>; // falseT extends string ? true : false liest sich fast wie der normale JavaScript-Ternäroperator (bedingung ? dann : sonst) – extends prüft hier NICHT Vererbung (wie bei Klassen, Kapitel 13), sondern "ist T ZUWEISBAR zu string?", ausgewertet auf TYP-Ebene.
Praxis: den Rückgabetyp einer Methode extrahieren
Wir bauen einen Conditional Type, der aus JEDER Repository<T>-Klasse (Kapitel 17-18) automatisch den ELEMENT-Typ extrahiert – nützlich, wenn man den Typ NUR aus der Repository-Klasse kennt, ohne ihn ein zweites Mal manuell hinzuschreiben:
import { Repository } from '../repository/Repository.js';
export type ExtrahiereElementTyp<R> = R extends Repository<infer T> ? T : never;
// Angewendet:
type BuchRepo = Repository<import('../models/Buch.js').Buch>;
type ExtrahierterTyp = ExtrahiereElementTyp<BuchRepo>; // Buchinfer T ist der SCHLÜSSEL zu Conditional Types: es sagt TypeScript "finde HERAUS, welcher konkrete Typ hier an dieser Position steht, und nenne ihn T" – infer kann NUR innerhalb der extends-Bedingung eines Conditional Type verwendet werden, nirgendwo sonst.
Verteilte Conditional Types bei Unions
type OhneArray<T> = T extends any[] ? never : T;
type Gemischt = string | number[] | boolean;
type Gefiltert = OhneArray<Gemischt>; // string | boolean - number[] wurde entfernt!Ein SUBTILES, aber wichtiges Detail: wird ein Conditional Type auf eine UNION angewendet, wird er AUTOMATISCH auf JEDES Mitglied der Union EINZELN angewendet und die Ergebnisse wieder zu einer Union zusammengeführt ("Distributive Conditional Types") – never aus Kapitel 4/19 verschwindet dabei aus der resultierenden Union, da string | never zu string vereinfacht wird.
Verteilung verhindern mit eckigen Klammern
type OhneArrayNichtVerteilt<T> = [T] extends [any[]] ? never : T;
type Ergebnis = OhneArrayNichtVerteilt<Gemischt>; // never - GESAMTE Union geprüft, nicht Mitglied für Mitglied[T] (T in eckige Klammern eingepackt) VERHINDERT die automatische Verteilung – manchmal gewünscht, wenn man die GESAMTE Union als EINEN Typ prüfen will, statt Mitglied-für-Mitglied. Ein fortgeschrittenes Detail, das selten gebraucht wird, aber überraschendes Verhalten erklärt, falls es einmal auftritt.
Eingebaute Beispiele: Awaited und NonNullable
type A = Awaited<Promise<string>>; // string - "packt" ein Promise aus
type B = NonNullable<string | null | undefined>; // string - entfernt null/undefinedAwaited und NonNullable aus Kapitel 20 sind SELBST als Conditional Types mit infer implementiert – jetzt verstehen Sie GENAU, wie sie intern funktionieren, nicht nur, wie man sie benutzt.
Tipp: Faustregel: Conditional Types SELBST schreiben ist FORTGESCHRITTEN und selten im alltäglichen Anwendungscode nötig – meist reicht ein bereits eingebauter Utility Type (Kapitel 20). Nützlich vor allem beim Schreiben WIEDERVERWENDBARER Bibliotheks-Typen, die sich an BELIEBIGE Eingabetypen anpassen sollen – GENAU wie wir es hier mit ExtrahiereElementTyp demonstriert haben.