as const
Eine Liste von Werten und eine Literal-Union beschreiben oft dasselbe. Ein Bestiarium, ein Buch der Monster, führt jede Monsterart auf, das Programm hat also eine Liste davon, während der Rest des Programms den Typ verwendet:
const monsterArten = ["wolf", "troll", "drache"];
type MonsterArt = "wolf" | "troll" | "drache";
const erfahrungFuer: Record<MonsterArt, number> = { wolf: 15, troll: 40, drache: 100 };
for (const monsterArt of monsterArten) {
console.log(`${monsterArt}: ${erfahrungFuer[monsterArt]} EP`);
}main.ts(7,35): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Record<MonsterArt, number>'.
No index signature with a parameter of type 'string' was found on type 'Record<MonsterArt, number>'.Es gibt zwei Probleme. Erstens sind die Monsterarten doppelt geschrieben, und eines Tages fügt jemand der Liste eine neue hinzu und vergisst den Typ, oder umgekehrt. Zweitens verbindet TypeScript die Liste nicht mit dem Typ. Für monsterArten leitet es string[] ab, ein Array beliebiger Texte, ein Element der Liste ist also keine MonsterArt, und das Lesen von erfahrungFuer[monsterArt] ist der Fehler aus der vorigen Übung.
Beide Probleme löst as const hinter dem Array:
const monsterArten = ["wolf", "troll", "drache"] as const;as const sagt TypeScript, dass es die genauen Werte behalten soll. Der Typ von monsterArten ist jetzt readonly ["wolf", "troll", "drache"]: ein schreibgeschütztes Tupel (Tupel kennst du aus der Lektion über Basistypen) aus genau diesen drei Texten. Schreibgeschützt bedeutet, dass sich die Liste nicht ändern kann:
const monsterArten = ["wolf", "troll", "drache"] as const;
monsterArten.push("greif");main.ts(2,14): error TS2339: Property 'push' does not exist on type 'readonly ["wolf", "troll", "drache"]'.Das willst du hier: Wenn sich die Liste ändern könnte, während das Programm läuft, könnte kein Typ sie im Voraus beschreiben.
Jetzt kannst du die Union aus der Liste gewinnen, sodass die Monsterarten nur einmal geschrieben werden:
type MonsterArt = (typeof monsterArten)[number];Lies es von innen nach außen. typeof monsterArten bedeutet in einem Typ „der Typ der Variablen monsterArten“. (Das ist das typeof von TypeScript für Typen. Das typeof von JavaScript, das beim Ausführen des Programms einen Text wie "string" liefert, ist etwas anderes mit demselben Namen.) [number] bedeutet dann „der Typ eines Elements an einem beliebigen Index“: "wolf" | "troll" | "drache". Es sieht kompliziert aus, ist aber immer dasselbe Rezept, (typeof liste)[number], und die Lektion über Utility-Typen erklärt beide Teile genauer.
Damit funktioniert das Bestiarium:
const monsterArten = ["wolf", "troll", "drache"] as const;
type MonsterArt = (typeof monsterArten)[number];
const erfahrungFuer: Record<MonsterArt, number> = { wolf: 15, troll: 40, drache: 100 };
for (const monsterArt of monsterArten) {
console.log(`${monsterArt}: ${erfahrungFuer[monsterArt]} EP`);
}Das gibt aus:
wolf: 15 EP
troll: 40 EP
drache: 100 EPUnd die Liste und der Typ können nicht mehr auseinanderlaufen. Füge "greif" zur Liste hinzu, und der Typ bekommt ihn auch, sodass TypeScript jede Tabelle meldet, in der das neue Monster fehlt:
main.ts(4,7): error TS2741: Property 'greif' is missing in type '{ wolf: number; troll: number; drache: number; }' but required in type 'Record<"drache" | "greif" | "troll" | "wolf", number>'.(In dieser Meldung schreibt TypeScript die ganze Union aus statt des Namens MonsterArt.)
Typischer Fehler: as const zu vergessen. Ohne es ist monsterArten ein string[], und (typeof monsterArten)[number] ist nur string. Es gibt keinen Fehler, aber der Typ erlaubt jeden Text, sogar const monsterArt: MonsterArt = "kartoffel";.
So schreibst du es:
Anweisungen
- 1
Führe das Programm aus: TypeScript kann die Tabellen nicht mit einem Element von
klassenlesen. Füge der Listeas consthinzu und ersetze die von Hand geschriebene Union inHeldenKlassedurch(typeof klassen)[number]. Das Programm gibt dann aus:Wähle deine Klasse: - Krieger (120 Leben) - Magier (70 Leben) - Waldläufer (90 Leben) - 2
Eine neue Klasse kommt dazu: Füge
"heiler"am Ende der Liste hinzu. Führe das Programm aus und füge hinzu, was TypeScript verlangt: Die Klasse heißtHeiler, und ein Heiler startet mit80Leben. Das Programm gibt dann aus:Wähle deine Klasse: - Krieger (120 Leben) - Magier (70 Leben) - Waldläufer (90 Leben) - Heiler (80 Leben)