Zapomenutý případ

Programy rostou. Řekněme, že do hry přibudou klíče: do sjednocení Predmet přidáte type Klic a teď potřebuje nový případ každý switch podle predmet.druh v celém programu. Najdete ve velkém programu všechny?

Hledat nemusíte: najde je TypeScript. První způsob už jste viděli, u funkce s návratovým typem, ve které každý případ vrací hodnotu. Tady klíč svůj případ ještě nemá:

type Lektvar = { druh: "lektvar"; nazev: string; leceni: number };
type Zbran = { druh: "zbran"; nazev: string; poskozeni: number };
type Klic = { druh: "klic"; nazev: string; otevira: string };
type Predmet = Lektvar | Zbran | Klic;

function popisPredmetu(predmet: Predmet): string {
    switch (predmet.druh) {
        case "lektvar":
            return `${predmet.nazev} (léčí ${predmet.leceni})`;
        case "zbran":
            return `${predmet.nazev} (poškození ${predmet.poskozeni})`;
    }
}
main.ts(6,43): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.

Pro klíč se neshoduje žádný případ a funkce by došla na konec, aniž by něco vrátila. To říká hláška. Pomůže, ale neřekne, který druh chybí. A ve funkci, která nic nevrací (void), se žádná hláška neobjeví: zapomenutý klíč prostě potichu nic neudělá.

Proto programátoři v TypeScriptu přidávají do větve default kontrolu. Funguje v každém příkazu switch a zapomenutý druh pojmenuje:

        default:
            const neosetreny: never = predmet;
            return neosetreny;

S těmito třemi řádky na konci příkazu switch výše TypeScript ohlásí:

main.ts(13,19): error TS2322: Type 'Klic' is not assignable to type 'never'.

Jak to funguje? never je zvláštní typ: typ hodnoty, která nemůže existovat. Nevejde se do něj žádná hodnota; i const neosetreny: never = 5; je chyba (Type '5' is not assignable to type 'never'.). Každý case předmět zúží, takže ve větvi default zbudou jen druhy, které svůj případ nemají. Když má každý druh svůj případ, nezbude nic: předmět tam má typ never a uložit ho do proměnné neosetreny („neošetřený“) jde bez problémů (a větev default se nikdy neprovede). Když na nějaký druh zapomenete, zbude, a do typu never se nevejde. Hláška zapomenutý typ pojmenuje: Klic.

A proč return neosetreny;? Díky němu vrací hodnotu každá větev funkce, takže TypeScript navíc neohlásí chybu TS2366 z prvního příkladu. Hodnota typu never se hodí ke každému návratovému typu a řádek funguje i ve funkci typu void.

Této kontrole se říká kontrola úplnosti (anglicky exhaustiveness check): TypeScript zkontroluje, že switch ošetřuje všechny druhy. Pište ji do každého příkazu switch nad rozlišeným sjednocením. Až pak jednou přidáte nový druh, stačí program spustit a TypeScript ukáže každý switch, který potřebuje nový případ.

Typické chyby:

  • Větev default, která vrací něco jiného, třeba default: return "neznámý předmět";. Pro TypeScript jsou pak zapomenuté druhy ošetřené a z klíče se potichu stane neznámý předmět.
  • Proměnná bez : never. const neosetreny = predmet; je obyčejná proměnná: dostane typ toho, co ve větvi default zbylo, a sama nic nekontroluje, protože bez : never nic neříká, že zbýt nesmí nic. Zapomenutý druh se pak ozve nanejvýš hláškou u return, třeba Type 'Klic' is not assignable to type 'string'., a z ní hned nepoznáte, že chybí case.

Pokyny

Přidejte kontrolu úplnosti, větev default s typem never, do obou příkazů switch: v popisPredmetu i v pouzijPredmet. Program vypíše pořád stejné řádky.

Teď do hry přibudou svitky. Přidejte do sjednocení Predmet typ type Svitek = { druh: "svitek"; nazev: string; kouzlo: string }; a na konec inventáře { druh: "svitek", nazev: "Svitek světla", kouzlo: "Světlo" }. Spusťte program a doplňte případy, o které si TypeScript řekne: popisPredmetu vrátí Svitek světla (kouzlo: Světlo) a pouzijPredmet vypíše Kira sešle kouzlo Světlo. Program pak vypíše:

Léčivý lektvar (léčí 20)
Stříbrný meč (poškození 12)
Starý klíč (otevírá kryptu)
Svitek světla (kouzlo: Světlo)
Kira vypije Léčivý lektvar
Kira tasí Stříbrný meč
Kira odemkne kryptu
Kira sešle kouzlo Světlo

Začněte programovat zdarma

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

Nebo se registrujte pomocí:

3/8