Indexed-Access-Typen
In der Lektion „Generics“ hast du Held[K] geschrieben: den Typ der Eigenschaft K von Held. Den Schlüssel in den eckigen Klammern kannst du auch gleich hinschreiben, als Literaltyp in Anführungszeichen. Held["heldenKlasse"] ist der Typ der Eigenschaft heldenKlasse von Held. Das nennt man einen Indexed-Access-Typ:
interface Held {
name: string;
heldenKlasse: "Krieger" | "Magier" | "Waldläufer";
stufe: number;
}
function ausbilder(heldenKlasse: Held["heldenKlasse"]): string {
if (heldenKlasse === "Krieger") {
return "Bruno, der Waffenmeister";
} else if (heldenKlasse === "Magier") {
return "Ema, die Erzmagierin";
}
return "Tom, der alte Jäger";
}
console.log(ausbilder("Magier"));
console.log(ausbilder("heiler"));main.ts(17,23): error TS2345: Argument of type '"heiler"' is not assignable to parameter of type '"Krieger" | "Magier" | "Waldläufer"'.Der Parameter akzeptiert genau die Klassen aus Held, und TypeScript schreibt sie in der Meldung aus. Warum nicht einfach die Union noch einmal schreiben? Weil die Klassen dann an zwei Stellen stünden. Mit Held["heldenKlasse"] stehen sie nur einmal da, in Held. Kommt dort eine neue Klasse hinzu, akzeptiert ausbilder sie auch, und niemand muss daran denken, es zu ändern.
Bei einer Union von Objekttypen, wie in der Lektion „Daten modellieren“, bekommst du die Union der Typen dieser Eigenschaft in allen Mitgliedern. Jeder Auftrag hat einen status, also ist Auftrag["status"] die Union aller Status:
type Auftrag =
| { status: "offen"; titel: string }
| { status: "erledigt"; titel: string; belohnung: number }
| { status: "gescheitert"; titel: string; grund: string };
type AuftragStatus = Auftrag["status"]; // "offen" | "erledigt" | "gescheitert"Nur die Eigenschaften, die jedes Mitglied hat, funktionieren so: Auftrag["belohnung"] ergibt Property 'belohnung' does not exist on type 'Auftrag'.
Zusammen mit Record aus der Lektion „Daten modellieren“ bekommst du ein Objekt mit einem Wert für jeden Status: Record<Auftrag["status"], number> hat die Schlüssel offen, erledigt und gescheitert. Das brauchst du im Abschlussprojekt.
Der Schlüssel kann auch der Typ number sein. Bei einem Array-Typ bedeutet [number] „das Element an irgendeinem Index“: Mit type Namen = string[]; ist Namen[number] gleich string. Das ist die zweite Hälfte des Rezepts (typeof klassen)[number] aus der Lektion „Daten modellieren“: typeof klassen ist der Typ der Liste, und [number] ist der Typ ihrer Elemente. Die erste Hälfte, typeof, kommt in der nächsten Übung.
Typische Fehler:
- Ein Punkt statt der eckigen Klammern. In Typen funktioniert
Held.heldenKlassenicht, aber die Meldung sagt dir, was du schreiben musst:main.ts(7,34): error TS2713: Cannot access 'Held.heldenKlasse' because 'Held' is a type, but not a namespace. Did you mean to retrieve the type of the property 'heldenKlasse' in 'Held' with 'Held["heldenKlasse"]'? - Der Schlüssel ohne Anführungszeichen,
Held[heldenKlasse]. Der Schlüssel ist ein Typ, also ein Literal in Anführungszeichen. Ohne sie liest TypeScriptheldenKlasseals Namen. Inausbilderist das der Name des Parameters, ein Wert, also bekommst du:
Wo nichtsmain.ts(7,39): error TS2538: Type 'heldenKlasse' cannot be used as an index type. main.ts(7,39): error TS2749: 'heldenKlasse' refers to a value, but is being used as a type here. Did you mean 'typeof heldenKlasse'?heldenKlasseheißt, bekommst du statt der TS2749-MeldungCannot find name 'heldenKlasse'. - Ein Schlüssel, den der Typ nicht hat,
Held["typ"]:Property 'typ' does not exist on type 'Held'.
So schreibst du es:
Anweisungen
- 1
Ändere den Typ des Parameters von
gibSeltenheitAusvonstringzuGegenstand["seltenheit"]und führe das Programm aus. TypeScript findet in einem der Aufrufe einen Tippfehler. Behebe ihn, dann gibt das Programm auch die legendären Gegenstände aus. - 2
Zähle unter den Aufrufen die Gegenstände jeder Seltenheit. Erstelle eine Konstante
anzahlenvom TypRecord<Gegenstand["seltenheit"], number>mit einer0für jede Seltenheit. Addiere in einerfor...of-Schleife überladenfür jeden Gegenstand eins zuanzahlen[gegenstand.seltenheit], und gibanzahlennach der Schleife aus:{ normal: 2, selten: 1, mythisch: 1 } - 3
Der Laden hat eine neue Seltenheit,
"episch". Füge sie der Union inGegenstandhinzu, füge am Ende vonladenden Gegenstand{ name: "Drachenschuppe", seltenheit: "episch", preis: 150 }hinzu und rufe unter den anderen beiden AufrufengibSeltenheitAus("episch")auf. TypeScript meldet die Stelle, die von der neuen Seltenheit wissen muss; behebe sie.gibSeltenheitAusbrauchte keine Änderung. Das Programm endet so:mythisch: - Phönixfeder (300 Gold) episch: - Drachenschuppe (150 Gold) { normal: 2, selten: 1, mythisch: 1, episch: 1 }