Kodokon kodokon.com

Literaltypen und diskriminierte Unions

Modelliere die Zustände deiner Anwendung mit Unions, die der Compiler für dich prüft.

8 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Ein Literaltyp schränkt einen Wert auf einen exakten Inhalt ein: 'info' statt string. Das ist die Grundlage für APIs, die man kaum falsch verwenden kann: Der Compiler weist jeden Wert außerhalb der Liste zurück, und dein Editor bietet dir Autovervollständigung an.

TYPESCRIPT
type Level = "info" | "warn" | "error";

function log(level: Level, message: string): void {
  console.log(`[${level}] ${message}`);
}

log("info", "Server started");
// log("fatal", "Boom"); -> rejected by the compiler
Eine Union aus Literalen legt die erlaubten Werte fest

Der nächste Schritt: Objekttypen kombinieren, die eine gemeinsame Literal-Eigenschaft teilen, den sogenannten Diskriminanten. Jeder Zustand eines Netzwerkaufrufs trägt zum Beispiel nur die Daten, die zu ihm gehören: Auf data zuzugreifen, während geladen wird, ist schlicht unmöglich.

TYPESCRIPT
type ApiState =
  | { status: "loading" }
  | { status: "success"; data: string[] }
  | { status: "error"; message: string };

function render(state: ApiState): string {
  switch (state.status) {
    case "loading":
      return "Loading...";
    case "success":
      return state.data.join(", ");
    case "error":
      return state.message;
  }
}
Eine über die Eigenschaft status diskriminierte Union

In jedem case verengt TypeScript den Typ: Nach case 'error' existiert state.message, state.data dagegen nicht. Dieser Mechanismus, Narrowing genannt, ersetzt fragile manuelle Prüfungen. Ergänze eine Vollständigkeitsprüfung, um das Ganze abzusichern.

TYPESCRIPT
function assertNever(value: never): never {
  throw new Error("Unhandled case: " + String(value));
}

function label(state: ApiState): string {
  switch (state.status) {
    case "loading": return "In progress";
    case "success": return "Done";
    case "error": return "Failed";
    default: return assertNever(state);
  }
}
Vollständigkeitsprüfung mit dem Typ never

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Welche Rolle spielt in einer diskriminierten Union die gemeinsame Eigenschaft wie status?
    • Die Laufzeit-Performance verbessern
    • Dem Compiler erlauben, den Typ in jedem Zweig zu verengen
    • Die Eigenschaft im empfangenen JSON verpflichtend machen
    • Doppelte Eigenschaften verhindern
  2. Welcher Aufruf von log(level, msg) kompiliert bei type Level = 'info' | 'warn'?
    • log('debug', msg)
    • log('info', msg)
    • log(someString, msg), wobei someString vom Typ string ist
  3. Wozu dient eine Funktion assertNever(value: never) im default-Zweig eines switch?
    • Fehler in der Produktion protokollieren
    • Aus einem vergessenen Fall einen Kompilierfehler machen
    • Den Wert in never umwandeln
    • Das Programm schneller beenden