Menu

Switch case in TypeScript: narrowing e controlli esaustivi

L'istruzione switch in TypeScript: sintassi, come ogni case restringe un tipo union, switch esaustivi che non compilano quando manca un case, il pattern switch (true), il fallthrough e lo scope dei blocchi.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Un'istruzione switch in TypeScript è lo switch di JavaScript con il controllo dei tipi. Confronta un valore con ogni case usando ===, esegue il ramo corrispondente e si ferma a break o return. TypeScript restringe il valore dello switch dentro ogni case e può verificare che ogni valore possibile sia gestito.

Le etichette impilate (case "sat": case "sun":) condividono un ramo. return termina la funzione, quindi dopo non serve break.

Sintassi

switch (expression) {
    case value1:
        // runs when expression === value1
        break;
    case value2:
    case value3:
        // runs for value2 or value3
        break;
    default:
        // runs when nothing else matched
}
  • Il confronto usa l'uguaglianza stretta, ===: case 1 non corrisponde alla stringa "1".
  • Senza break, return o throw, l'esecuzione prosegue nel case successivo (fallthrough).
  • default è opzionale e può stare ovunque, anche se per convenzione va in fondo.
  • Un valore di case che TypeScript può dimostrare non corrispondere mai è l'errore TS2678. Per un parametro di tipo "a" | "b", case "c": segnala Type '"c"' is not comparable to type '"a" | "b"'., e questo intercetta i refusi nelle etichette.

Narrowing dentro ogni case

In ogni case, TypeScript sa quale valore ha corrisposto e restringe il tipo. È particolarmente utile con una discriminated union: fai lo switch sulla proprietà tag comune, e ogni case vede la variante corrispondente con le sue proprietà.

shape.radius compila solo nel case "circle". Fuori da lì, shape potrebbe essere un rettangolo, che non ha radius. Non c'è nessun default, eppure la funzione supera il controllo dei tipi come funzione che restituisce number, perché TypeScript vede che i tre case coprono ogni kind. Altri pattern basati su questo sono nella pagina sulle discriminated union.

Switch esaustivo con never

La funzione qui sopra smette di compilare se si aggiunge una quarta forma senza il suo case (TS2366, un return mancante). Funziona solo quando la funzione restituisce un valore. Per una garanzia che valga anche nel codice void e dia un messaggio più chiaro, aggiungi un default che assegna il valore a never:

index.ts(14,19): error TS2322: Type '{ kind: "triangle"; base: number; height: number; }' is not assignable to type 'never'.

Dopo i case gestiti, l'unico tipo rimasto per shape è la variante triangolo, che non si può assegnare a never. L'errore indica esattamente cosa manca. Aggiungi case "triangle": return (shape.base * shape.height) / 2; e il default vede never, quindi il blocco compila e stampa 9. Il throw protegge comunque dai dati sbagliati in esecuzione, come un kind arrivato da un JSON che i tipi non avevano previsto.

Molte codebase racchiudono il controllo in una funzione di supporto:

La stessa tecnica funziona con gli enum: fai lo switch sul valore dell'enum e passalo ad assertNever nel default.

switch (true) per intervalli e condizioni

switch confronta valori, quindi per gli intervalli serve un trucco: fai lo switch su true e scrivi una condizione in ogni case. Da TypeScript 5.3, quelle condizioni restringono i tipi proprio come le istruzioni if.

I case vengono provati in ordine, quindi metti per primi i più specifici. Che si legga meglio di if / else if è questione di gusti; il comportamento è lo stesso.

Fallthrough e break

Un case con del codice ma senza break, return o throw prosegue nel case successivo. È quasi sempre un bug. L'opzione del compilatore noFallthroughCasesInSwitch (non inclusa in strict) lo trasforma nell'errore TS7029, Fallthrough case in switch., pur continuando ad ammettere le etichette vuote impilate.

Un fallthrough voluto come questo funziona, ma con noFallthroughCasesInSwitch attivo va riscritto, per esempio con controlli if separati. Di solito è anche più chiaro.

Variabili dentro i case

L'intero corpo dello switch è un unico blocco, quindi una const dichiarata in un case è visibile negli altri (e ridichiararla è un errore). Racchiudi un case tra graffe per dargli uno scope proprio:

Senza le graffe, la seconda const unit è l'errore di compilazione TS2451, Cannot redeclare block-scoped variable 'unit'.

switch o lookup su un oggetto

Quando ogni case si limita a mappare un valore su un altro, un oggetto tipizzato con Record è più corto, e TypeScript controlla che ogni chiave sia presente:

Omettere una chiave è un errore di compilazione, che dà la stessa esaustività di un controllo con never. Tieni lo switch per i casi che eseguono logica diversa, restringono varianti di una union o fanno return anticipati.

Domande frequenti

Come si scrive un'istruzione switch in TypeScript?

Esattamente come in JavaScript: switch (value) { case "a": ...; break; default: ... }. I case vengono confrontati con ===. TypeScript aggiunge dei controlli: un valore di case che non può mai corrispondere al tipo dello switch è un errore (TS2678), e dentro ogni case la variabile dello switch viene ristretta a quel case.

Come rendo esaustivo uno switch in TypeScript?

Aggiungi un default che assegna il valore a una variabile di tipo never: default: { const unreachable: never = value; throw new Error(...) }. Quando ogni membro della union è gestito, lì il valore è never e il codice compila. Quando ne manca uno, il compilatore segnala il membro mancante (TS2322).

Come gestisco più case con lo stesso codice in uno switch TypeScript?

Impila le etichette senza codice in mezzo: case "sat": case "sun": return "weekend";. I case vuoti passano al successivo. L'opzione noFallthroughCasesInSwitch segnala solo i case che hanno codice e nessun break o return, quindi le etichette impilate restano ammesse.

switch (true) restringe i tipi in TypeScript?

Sì, da TypeScript 5.3. In switch (true) { case typeof x === "string": ... }, dentro quel case x è ristretto a string, proprio come succederebbe in un if. È un'alternativa leggibile a una catena di if/else if con controlli su intervalli o tipi.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA