EMZETT.
Login

Kurz: Ein Wert kann einer von mehreren Typen sein:

Teil des Kurses TypeScript

Kapitel 5 von 15 im Kurs TypeScript. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

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üfungVerengt auf
typeof x === "string"string (auch number, boolean, function)
x instanceof DateKlasse
Array.isArray(x)Array
"name" in xTyp 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
  • never im default erzwingt Vollständigkeit
  • Eigene Typprüfer: x is Typ

Übungsaufgabe

Modelliere eine Zahlungsart als diskriminierte Union (karte, paypal, rechnung) mit unterschiedlichen Feldern und schreibe eine Funktion, die jede Art als Text beschreibt.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Objekte, Interfaces und Typ-Aliase

Weiter: Arrays, Tupel und Enums

Alle Kapitel: TypeScript im Überblick