Typy podle klíče
V lekci o generikách jste psali Hrdina[K]: typ vlastnosti K v typu Hrdina. Klíč v hranatých závorkách můžete napsat i rovnou, jako literálový typ v uvozovkách. Hrdina["povolani"] je typ vlastnosti povolani v typu Hrdina. Anglicky se mu říká indexed access type, česky třeba typ podle klíče:
interface Hrdina {
jmeno: string;
povolani: "valecnik" | "mag" | "hranicar";
uroven: number;
}
function ucitel(povolani: Hrdina["povolani"]): string {
if (povolani === "valecnik") {
return "Bruno, mistr zbraní";
} else if (povolani === "mag") {
return "Ema, mistryně magie";
}
return "Tom, starý lovec";
}
console.log(ucitel("mag"));
console.log(ucitel("lecitel"));main.ts(17,20): error TS2345: Argument of type '"lecitel"' is not assignable to parameter of type '"hranicar" | "mag" | "valecnik"'.Parametr přijme přesně povolání z typu Hrdina a TypeScript je v hlášce vypíše. Proč sjednocení nenapsat znovu? Protože pak by povolání byla na dvou místech. S Hrdina["povolani"] jsou napsaná jen jednou, v typu Hrdina. Když tam přibude nové povolání, funkce ucitel ho přijme taky a nikdo nemusí myslet na to, že ji má upravit.
U sjednocení objektových typů, jako v lekci Modelování dat, dostanete sjednocení typů té vlastnosti ve všech jeho členech. Každý úkol má stav, takže Ukol["stav"] je sjednocení všech stavů:
type Ukol =
| { stav: "otevreny"; nazev: string }
| { stav: "splneny"; nazev: string; odmena: number }
| { stav: "neuspesny"; nazev: string; duvod: string };
type StavUkolu = Ukol["stav"]; // "otevreny" | "splneny" | "neuspesny"Takhle fungují jen vlastnosti, které mají všichni členové: Ukol["odmena"] skončí chybou Property 'odmena' does not exist on type 'Ukol'.
Spolu s Record z lekce Modelování dat tak vznikne objekt s jednou hodnotou pro každý stav: Record<Ukol["stav"], number> má klíče otevreny, splneny a neuspesny. Použijete ho v závěrečném projektu.
Klíčem může být i typ number. U typu pole znamená [number] „prvek na libovolném indexu“: s type Jmena = string[]; je Jmena[number] typ string. To je druhá polovina receptu (typeof vsechnaPovolani)[number] z lekce Modelování dat: typeof vsechnaPovolani je typ seznamu a [number] typ jeho prvků. První polovina, typeof, přijde na řadu v příštím cvičení.
Typické chyby:
- Tečka místo hranatých závorek. V typech
Hrdina.povolaninefunguje, ale hláška vám poradí, co napsat:main.ts(7,27): error TS2713: Cannot access 'Hrdina.povolani' because 'Hrdina' is a type, but not a namespace. Did you mean to retrieve the type of the property 'povolani' in 'Hrdina' with 'Hrdina["povolani"]'? - Klíč bez uvozovek,
Hrdina[povolani]. Klíč je typ, takže je to literál v uvozovkách. Bez nich TypeScript hledá typ jménempovolani. Ve funkciucitelvýše, kde se tak jmenuje parametr, ohlásí'povolani' refers to a value, but is being used as a type here., jindeCannot find name 'povolani'. - Klíč, který typ nemá,
Hrdina["trida"]:Property 'trida' does not exist on type 'Hrdina'.
Jak psát:
Pokyny
Změňte typ parametru funkce vypisVzacnost ze string na Predmet["vzacnost"] a spusťte program. TypeScript v jednom z volání najde překlep. Opravte ho a program vypíše i legendární předměty. (Vzácnost se vypíše tak, jak je v kódu, bez diakritiky, třeba legendarni:.)
Pod voláními spočítejte předměty každé vzácnosti. Vytvořte konstantu pocty typu Record<Predmet["vzacnost"], number> s nulou pro každou vzácnost. V cyklu for...of přes obchod pro každý předmět přičtěte jedničku k pocty[predmet.vzacnost] a za cyklem pocty vypište:
{ bezny: 2, vzacny: 1, legendarni: 1 }Obchod má novou vzácnost, "epicky". Přidejte ji do sjednocení v typu Predmet, na konec pole obchod přidejte předmět { nazev: "Dračí šupina", vzacnost: "epicky", cena: 150 } a pod ostatní dvě volání přidejte vypisVzacnost("epicky"). TypeScript ohlásí místo, které se o nové vzácnosti musí dozvědět; opravte ho. Funkci vypisVzacnost jste měnit nemuseli. Program končí takto:
legendarni:
- Fénixovo pero (300 zlatých)
epicky:
- Dračí šupina (150 zlatých)
{ bezny: 2, vzacny: 1, legendarni: 1, epicky: 1 }Začněte programovat zdarma
4/7