as const

Seznam hodnot a sjednocení literálů často popisují totéž. Bestiář, kniha o příšerách, vypisuje všechny typy příšer, a tak má program jejich seznam, zatímco zbytek programu používá typ:

const typyPriser = ["vlk", "troll", "drak"];
type TypPrisery = "vlk" | "troll" | "drak";

const zkusenostiZa: Record<TypPrisery, number> = { vlk: 15, troll: 40, drak: 100 };

for (const typ of typyPriser) {
    console.log(`${typ}: ${zkusenostiZa[typ]} zkušeností`);
}
main.ts(7,28): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Record<TypPrisery, number>'.
  No index signature with a parameter of type 'string' was found on type 'Record<TypPrisery, number>'.

Má to dvě potíže. Zaprvé jsou typy příšer napsané dvakrát a jednou někdo přidá nový do seznamu a na typ zapomene, nebo naopak. Zadruhé TypeScript seznam s typem nijak nespojí. Pro typyPriser odvodí string[], tedy pole jakýchkoli textů, takže prvek seznamu není TypPrisery a čtení zkusenostiZa[typ] je chyba z minulého cvičení.

Obě potíže vyřeší as const za polem:

const typyPriser = ["vlk", "troll", "drak"] as const;

as const říká TypeScriptu, aby si zapamatoval přesné hodnoty. Proměnná typyPriser má teď typ readonly ["vlk", "troll", "drak"]: n-tice jen pro čtení (n-tice znáte z lekce Základní typy) přesně s těmito třemi texty. Jen pro čtení znamená, že se seznam nemůže změnit:

const typyPriser = ["vlk", "troll", "drak"] as const;
typyPriser.push("gryf");
main.ts(2,12): error TS2339: Property 'push' does not exist on type 'readonly ["vlk", "troll", "drak"]'.

Přesně to tu chcete: kdyby se seznam mohl za běhu programu měnit, žádný typ by ho nemohl předem popsat.

Teď můžete sjednocení ze seznamu získat, takže typy příšer napíšete jen jednou:

type TypPrisery = (typeof typyPriser)[number];

Čtěte to zevnitř. typeof typyPriser v typu znamená „typ proměnné typyPriser“. (Je to typeof TypeScriptu pro typy. Operátor typeof z JavaScriptu, který za běhu programu vrací text jako "string", je něco jiného, jen se stejným názvem.) [number] pak znamená „typ prvku na libovolném indexu“: "vlk" | "troll" | "drak". Vypadá to složitě, ale je to pořád stejný recept, (typeof seznam)[number], a obě části podrobněji vysvětlí lekce Pomocné typy.

S tím bestiář funguje:

const typyPriser = ["vlk", "troll", "drak"] as const;
type TypPrisery = (typeof typyPriser)[number];

const zkusenostiZa: Record<TypPrisery, number> = { vlk: 15, troll: 40, drak: 100 };

for (const typ of typyPriser) {
    console.log(`${typ}: ${zkusenostiZa[typ]} zkušeností`);
}

Vypíše:

vlk: 15 zkušeností
troll: 40 zkušeností
drak: 100 zkušeností

A seznam a typ se už nemůžou rozejít. Přidejte do seznamu "gryf" a typ ho dostane také, takže TypeScript ohlásí každou tabulku, ve které nová příšera chybí:

main.ts(4,7): error TS2741: Property 'gryf' is missing in type '{ vlk: number; troll: number; drak: number; }' but required in type 'Record<"drak" | "gryf" | "troll" | "vlk", number>'.

(V této hlášce TypeScript místo názvu TypPrisery vypisuje celé sjednocení.)

Typická chyba je zapomenuté as const. Bez něj je typyPriser typu string[] a (typeof typyPriser)[number] je jen string. Žádná chyba se neobjeví, ale typ dovolí jakýkoli text, třeba i const typ: TypPrisery = "brambora";.

Pokyny

Spusťte program: TypeScript neumí číst z tabulek pomocí prvku seznamu vsechnaPovolani. Přidejte k seznamu as const a ručně napsané sjednocení v typu Povolani nahraďte výrazem (typeof vsechnaPovolani)[number]. Program pak vypíše:

Vyberte si povolání:
- Válečník (120 životů)
- Mág (70 životů)
- Hraničář (90 životů)

Přibylo nové povolání: přidejte na konec seznamu "lecitel". Spusťte program a doplňte, o co si TypeScript řekne: povolání se jmenuje Léčitel a léčitel začíná s 80 životy. Program pak vypíše:

Vyberte si povolání:
- Válečník (120 životů)
- Mág (70 životů)
- Hraničář (90 životů)
- Léčitel (80 životů)

Začněte programovat zdarma

Registrací souhlasíte s provozními podmínkami a zásadami ochrany osobních údajů.

Nebo se registrujte pomocí:

5/8