Type Guards
typeof unterscheidet Texte von Zahlen. Aber was ist mit einer Union aus zwei Objekttypen? Für beide liefert typeof "object":
type Trank = { name: string; heilung: number };
type Waffe = { name: string; schaden: number };
type Gegenstand = Trank | Waffe;
const inventar: Gegenstand[] = [
{ name: "Heiltrank", heilung: 20 },
{ name: "Silberschwert", schaden: 12 },
];Bei einem Gegenstand darfst du name lesen, den beide Typen haben, aber nicht heilung. In for (const gegenstand of inventar) ergibt die Zeile console.log(gegenstand.heilung);:
main.ts(11,28): error TS2339: Property 'heilung' does not exist on type 'Gegenstand'.
Property 'heilung' does not exist on type 'Waffe'.JavaScript hat einen Operator, der prüft, ob ein Objekt eine Eigenschaft hat: "heilung" in gegenstand ist true, wenn gegenstand eine Eigenschaft namens heilung hat. TypeScript grenzt damit ein. Nur ein Trank hat heilung, also ist der Gegenstand innerhalb des if ein Trank und im else-Zweig eine Waffe:
for (const gegenstand of inventar) {
if ("heilung" in gegenstand) {
console.log(`${gegenstand.name} heilt ${gegenstand.heilung}`);
} else {
console.log(`${gegenstand.name} verursacht ${gegenstand.schaden} Schaden`);
}
}Das gibt aus:
Heiltrank heilt 20
Silberschwert verursacht 12 SchadenWenn du dieselbe Prüfung an mehreren Stellen brauchst, willst du sie in eine Funktion stecken. Aber eine Funktion, die boolean zurückgibt, grenzt nichts ein:
function istTrank(gegenstand: Gegenstand): boolean {
return "heilung" in gegenstand;
}
for (const gegenstand of inventar) {
if (istTrank(gegenstand)) {
console.log(`${gegenstand.name} heilt ${gegenstand.heilung}`);
}
}TypeScript meldet wieder Property 'heilung' does not exist on type 'Gegenstand'. Aus dem Rückgabetyp boolean erfährt es nur, dass die Funktion true oder false zurückgibt, nicht, was das über den Gegenstand aussagt. Dafür schreibst du einen besonderen Rückgabetyp, gegenstand is Trank:
function istTrank(gegenstand: Gegenstand): gegenstand is Trank {
return "heilung" in gegenstand;
}Die Funktion gibt weiterhin true oder false zurück, aber jetzt weiß TypeScript: Wenn sie true zurückgibt, ist gegenstand ein Trank. Eine solche Funktion heißt Type Guard. Mit ihr grenzt if (istTrank(gegenstand)) genauso ein wie die in-Prüfung darin, und im else-Zweig ist der Gegenstand eine Waffe. Der Name vor is ist der Name des Parameters.
(Wenn du den Rückgabetyp ganz weglässt, können aktuelle Versionen von TypeScript manchmal selbst auf gegenstand is Trank kommen. Mit : boolean hast du ihnen gesagt, dass es nur ein Boolean ist.)
Typische Fehler:
- TypeScript vertraut einem Type Guard: Es prüft nicht, ob der Rumpf das Richtige testet. Mit
return "schaden" in gegenstand;aus Versehen behandelt TypeScript Schwerter als Tränke, und die Schleife oben gibtSilberschwert heilt undefinedaus. - Auch ein Tippfehler im Namen der Eigenschaft wird nicht gemeldet.
"heilng" in gegenstandist für jeden Gegenstand einfachfalse, also ist nie ein Gegenstand ein Trank.
In der nächsten Lektion siehst du den häufigeren Weg, Objekttypen zu unterscheiden: eine Eigenschaft wie art, die sagt, was jedes Objekt ist. Dort findet TypeScript solche Tippfehler.
So schreibst du es:
Anweisungen
- 1
In der Schlacht gibt es Helden und Monster. Schreibe einen Type Guard
istMonstermit einem Parameterkaempfervom TypKaempfer: Er prüft mitin, ob der Kämpfer einMonsterist. - 2
Gehe mit
for...ofdurchschlachtund gib mitistMonsterjeden Kämpfer aus: ein Monster mit seiner Stärke, einen Helden mit seiner Stufe. Das Programm gibt dann aus:Kira (Stufe 3) Goblin (Stärke 4) Bruno (Stufe 2) Troll (Stärke 15) - 3
Addiere die Stärke aller Monster und gib sie nach der Schleife aus:
Kira (Stufe 3) Goblin (Stärke 4) Bruno (Stufe 2) Troll (Stärke 15) Monsterstärke: 19