Ein vergessener Fall
Programme wachsen. Angenommen, das Spiel bekommt Schlüssel: Du fügst der Union Gegenstand einen type Schluessel hinzu, und jetzt braucht jedes switch auf gegenstand.art im Programm einen neuen Fall. Wirst du in einem großen Programm alle finden?
Du musst nicht suchen: TypeScript findet sie für dich. Den ersten Weg hast du schon gesehen, in einer Funktion mit Rückgabetyp, in der jeder Fall etwas zurückgibt. Hier hat der Schlüssel seinen Fall noch nicht:
type Trank = { art: "trank"; name: string; heilung: number };
type Waffe = { art: "waffe"; name: string; schaden: number };
type Schluessel = { art: "schluessel"; name: string; oeffnet: string };
type Gegenstand = Trank | Waffe | Schluessel;
function beschreibe(gegenstand: Gegenstand): string {
switch (gegenstand.art) {
case "trank":
return `${gegenstand.name} (heilt ${gegenstand.heilung})`;
case "waffe":
return `${gegenstand.name} (Schaden ${gegenstand.schaden})`;
}
}main.ts(6,46): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.Für einen Schlüssel passt kein Fall, und die Funktion würde ihr Ende erreichen, ohne etwas zurückzugeben. Das sagt die Meldung. Sie hilft, aber sie sagt nicht, welche Art fehlt. Und in einer Funktion, die nichts zurückgibt (void), gibt es gar keine Meldung: Ein vergessener Schlüssel tut einfach stillschweigend nichts.
Deshalb fügen TypeScript-Programmierer dem default-Zweig eine Prüfung hinzu. Sie funktioniert in jedem switch und nennt die vergessene Art:
default:
const unbehandelt: never = gegenstand;
return unbehandelt;Mit diesen drei Zeilen am Ende des obigen switch meldet TypeScript:
main.ts(13,19): error TS2322: Type 'Schluessel' is not assignable to type 'never'.Wie funktioniert das? never ist ein besonderer Typ: der Typ eines Werts, der nicht existieren kann. Kein Wert passt hinein; sogar const unbehandelt: never = 5; ist ein Fehler (Type '5' is not assignable to type 'never'.). Jeder case grenzt den Gegenstand ein, in default bleiben also nur die Arten übrig, die keinen Fall haben. Hat jede Art ihren Fall, bleibt nichts übrig: Der Gegenstand hat dort den Typ never, und ihn in unbehandelt zu speichern ist in Ordnung (und default läuft nie). Wenn du eine Art vergisst, bleibt diese Art übrig, und sie passt nicht in never. Die Meldung nennt den vergessenen Typ: Schluessel.
Und warum return unbehandelt;? Dadurch gibt jeder Zweig der Funktion etwas zurück, und TypeScript fügt den Fehler TS2366 von oben nicht zusätzlich hinzu. Ein never-Wert passt zu jedem Rückgabetyp, und die Zeile funktioniert auch in einer void-Funktion.
Diese Prüfung heißt Vollständigkeitsprüfung (englisch exhaustiveness check): TypeScript prüft, dass das switch jede Art behandelt. Schreibe sie in jedes switch auf einer diskriminierten Union. Wenn du dann eines Tages eine neue Art hinzufügst, führst du einfach das Programm aus, und TypeScript listet jedes switch auf, das einen neuen Fall braucht.
Typische Fehler:
- Ein
default, das etwas anderes zurückgibt, etwadefault: return "unbekannter Gegenstand";. Für TypeScript sind die vergessenen Arten dann behandelt, und ein Schlüssel wird stillschweigend zuunbekannter Gegenstand. - Die Variable ohne
: never.const unbehandelt = gegenstand;ist eine gewöhnliche Variable, die einfach den Typ dessen bekommt, was übrig bleibt, die Zeile selbst prüft also nichts. Erst das: neversagt: „Hier darf nichts übrig bleiben.“
So schreibst du es:
Anweisungen
- 1
Füge die Vollständigkeitsprüfung, den
default-Zweig mitnever, beiden Switches hinzu: inbeschreibeund inbenutze. Das Programm gibt weiterhin dieselben Zeilen aus. - 2
Jetzt bekommt das Spiel Schriftrollen. Füge der Union
Gegenstandtype Schriftrolle = { art: "schriftrolle"; name: string; zauber: string };hinzu und füge{ art: "schriftrolle", name: "Schriftrolle des Lichts", zauber: "Licht" }am Ende des Inventars hinzu. Führe das Programm aus und füge die Fälle hinzu, die TypeScript verlangt:beschreibegibtSchriftrolle des Lichts (Zauber: Licht)zurück, undbenutzegibtKira wirkt den Zauber Lichtaus. Das Programm gibt dann aus:Heiltrank (heilt 20) Silberschwert (Schaden 12) Alter Schlüssel (öffnet die Gruft) Schriftrolle des Lichts (Zauber: Licht) Kira trinkt: Heiltrank Kira zieht: Silberschwert Kira öffnet die Gruft Kira wirkt den Zauber Licht