Union-Typen
Ein Wert kann einer von mehreren Typen sein:
function formatiere(wert: string | number): string {
if (typeof wert === "string") {
return wert.toUpperCase(); // hier ist wert: string
}
return wert.toFixed(2); // hier ist wert: number
}
console.log(formatiere("hallo"), formatiere(3.14159));Ausgabe
HALLO 3.14
Innerhalb der if-Zweige verengt TypeScript den Typ (Narrowing). Das funktioniert mit:
| Prüfung | Verengt auf |
|---|---|
typeof x === "string" | string (auch number, boolean, function) |
x instanceof Date | Klasse |
Array.isArray(x) | Array |
"name" in x | Typ mit Eigenschaft name |
x !== null, x != null, if (x) | ohne null/undefined |
x === "a" | der Literaltyp |
function laenge(x: string | string[] | null): number {
if (x === null) return 0;
if (Array.isArray(x)) return x.length;
return x.length;
}
console.log(laenge(null), laenge(["a", "b"]), laenge("Hallo"));Ausgabe
0 2 5
Literaltypen
Typen können einzelne feste Werte sein:
type Richtung = "links" | "rechts" | "oben" | "unten";
type Wuerfel = 1 | 2 | 3 | 4 | 5 | 6;
function gehe(r: Richtung, schritte: number = 1): string {
return `${schritte} Schritt(e) nach ${r}`;
}
const w: Wuerfel = 4;
console.log(gehe("links"), gehe("oben", 3), w);Ausgabe
1 Schritt(e) nach links 3 Schritt(e) nach oben 4
type Richtung = "links" | "rechts";
function gehe(r: Richtung) {}
gehe("hinten");Fehlermeldung
error TS2345: Argument of type '"hinten"' is not assignable to parameter of type 'Richtung'.
Das ist viel sicherer als einfache Strings: Tippfehler fallen auf, und die Autovervollständigung zeigt alle erlaubten Werte.
Diskriminierte Unions
Das mächtigste Muster: Jede Variante hat ein gemeinsames Feld (Diskriminante), an dem sie erkannt wird:
type Form =
| { art: "kreis"; radius: number }
| { art: "rechteck"; breite: number; hoehe: number }
| { art: "dreieck"; basis: number; hoehe: number };
function flaeche(f: Form): number {
switch (f.art) {
case "kreis":
return Math.PI * f.radius ** 2;
case "rechteck":
return f.breite * f.hoehe;
case "dreieck":
return (f.basis * f.hoehe) / 2;
}
}
console.log(flaeche({ art: "kreis", radius: 1 }).toFixed(2));
console.log(flaeche({ art: "rechteck", breite: 3, hoehe: 4 }));
console.log(flaeche({ art: "dreieck", basis: 6, hoehe: 5 }));Ausgabe
3.14 12 15
In jedem case kennt TypeScript die passende Variante (f.radius gibt es nur beim Kreis).
Vollständigkeit prüfen mit never
Wenn du später eine neue Form ergänzt und einen case vergisst, soll der Compiler warnen:
function pruefe(x: never): never {
throw new Error("Nicht behandelt: " + JSON.stringify(x));
}
type Ampel = "rot" | "gelb" | "gruen";
function aktion(a: Ampel): string {
switch (a) {
case "rot": return "halt";
case "gelb": return "achtung";
case "gruen": return "los";
default: return pruefe(a); // Fehler, sobald ein Fall fehlt
}
}
console.log(aktion("rot"), aktion("gruen"));Ausgabe
halt los
Eigene Typprüfer (Type Guards)
interface Hund { bellen(): string }
interface Katze { miauen(): string }
function istHund(t: Hund | Katze): t is Hund {
return "bellen" in t;
}
function laut(t: Hund | Katze): string {
return istHund(t) ? t.bellen() : t.miauen();
}
console.log(laut({ bellen: () => "Wuff" }), laut({ miauen: () => "Miau" }));Ausgabe
Wuff Miau
t is Hund sagt dem Compiler: Wenn die Funktion true liefert, ist t ein Hund.
Ergebnis oder Fehler
Mit diskriminierten Unions lässt sich Fehlerbehandlung ohne Ausnahmen abbilden:
type Ergebnis<T> = { ok: true; wert: T } | { ok: false; fehler: string };
function teile(a: number, b: number): Ergebnis<number> {
return b === 0 ? { ok: false, fehler: "Division durch 0" } : { ok: true, wert: a / b };
}
for (const [a, b] of [[10, 2], [1, 0]]) {
const r = teile(a, b);
console.log(r.ok ? `Ergebnis ${r.wert}` : `Fehler: ${r.fehler}`);
}Ausgabe
Ergebnis 5 Fehler: Division durch 0
Merke
A | B: Wert ist einer der Typen; innerhalb von Prüfungen wird der Typ eingeengt (Narrowing)- Literaltypen (
"links" | "rechts") ersetzen magische Strings - Diskriminierte Unions: gemeinsames Feld (
art) bestimmt die Variante neverimdefaulterzwingt Vollständigkeit- Eigene Typprüfer:
x is Typ
Aufgabe
Modelliere eine Zahlungsart als diskriminierte Union (karte, paypal, rechnung) mit unterschiedlichen Feldern und schreibe eine Funktion, die jede Art als Text beschreibt.