Uma instrução switch em TypeScript é o switch do JavaScript com verificação de tipos. Ele compara um valor com cada case usando ===, executa o ramo correspondente e para em break ou return. O TypeScript estreita o valor avaliado dentro de cada case e pode verificar se todos os valores possíveis foram tratados.
Labels empilhados (case "sat": case "sun":) compartilham um ramo. return encerra a função, então não é preciso break depois dele.
Sintaxe
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
}
- A comparação usa igualdade estrita,
===:case 1não corresponde à string"1". - Sem
break,returnouthrow, a execução continua no case seguinte (fallthrough). defaulté opcional e pode ficar em qualquer posição, embora o normal seja no fim.- Um valor de
caseque o TypeScript consegue provar que nunca corresponde é o erro TS2678. Para um parâmetro do tipo"a" | "b",case "c":geraType '"c"' is not comparable to type '"a" | "b"'., o que pega erros de digitação nos labels.
Narrowing dentro de cada case
Em cada case, o TypeScript sabe qual valor correspondeu e estreita o tipo. Isso é mais útil com uma discriminated union: faça o switch na propriedade de tag compartilhada, e cada case enxerga a variante correspondente com suas próprias propriedades.
shape.radius só compila no case "circle". Fora dele, shape pode ser um retângulo, que não tem radius. Não há default, e mesmo assim a função é aceita como retornando number, porque o TypeScript vê que os três cases cobrem todos os valores de kind. Mais padrões construídos sobre isso estão na página de discriminated unions.
Switch exaustivo com never
A função acima deixa de compilar se uma quarta forma for adicionada sem um case (TS2366, falta de return). Isso só funciona quando a função retorna um valor. Para uma garantia que também funcione em código void e dê uma mensagem mais clara, adicione um default que atribui o valor a never:
index.ts(14,19): error TS2322: Type '{ kind: "triangle"; base: number; height: number; }' is not assignable to type 'never'.
Depois dos cases tratados, o único tipo que sobra para shape é a variante do triângulo, que não pode ser atribuída a never. O erro diz exatamente o que está faltando. Adicione case "triangle": return (shape.base * shape.height) / 2; e o default passa a ver never, então o bloco compila e imprime 9. O throw continua protegendo contra dados ruins em tempo de execução, como um kind vindo de um JSON que os tipos não previam.
Muitos projetos colocam essa verificação em uma função auxiliar:
A mesma técnica funciona com enums: faça o switch no valor do enum e passe-o para assertNever no default.
switch (true) para faixas e condições
switch compara valores, então faixas exigem um truque: faça o switch em true e escreva uma condição em cada case. Desde o TypeScript 5.3, essas condições estreitam tipos do mesmo jeito que as instruções if.
Os cases são testados em ordem, então coloque os mais específicos primeiro. Se isso fica mais legível que if / else if é questão de gosto; o comportamento é o mesmo.
Fallthrough e break
Um case com código, mas sem break, return ou throw, passa direto para o case seguinte. Isso quase sempre é um bug. A opção de compilador noFallthroughCasesInSwitch (que não faz parte de strict) transforma isso no erro TS7029, Fallthrough case in switch., mas continua permitindo labels vazios empilhados.
Um fallthrough proposital como este funciona, mas com noFallthroughCasesInSwitch ativada ele precisa ser reescrito, por exemplo com verificações if separadas. Em geral isso fica mais claro de qualquer forma.
Variáveis dentro dos cases
O corpo inteiro do switch é um único bloco, então um const declarado em um case é visível nos outros (e declarar de novo é erro). Envolva um case em chaves para dar a ele seu próprio escopo:
Sem as chaves, o segundo const unit é o erro de compilação TS2451, Cannot redeclare block-scoped variable 'unit'.
switch vs busca em objeto
Quando cada case só mapeia um valor para outro, um objeto tipado com Record é mais curto, e o TypeScript verifica se todas as chaves estão presentes:
Omitir uma chave é erro de compilação, o que dá a mesma exaustividade de uma verificação com never. Use switch para cases que executam lógicas diferentes, estreitam variantes de uma union ou retornam antes.
Perguntas frequentes
Como escrever uma instrução switch no TypeScript?
Exatamente como no JavaScript: switch (value) { case "a": ...; break; default: ... }. Os cases são comparados com ===. O TypeScript acrescenta verificações: um valor de case que nunca pode corresponder ao tipo avaliado é erro (TS2678), e dentro de cada case a variável avaliada é estreitada para aquele case.
Como tornar um switch exaustivo no TypeScript?
Adicione um default que atribui o valor a uma variável do tipo never: default: { const unreachable: never = value; throw new Error(...) }. Quando todos os membros da union são tratados, o valor ali é never e compila. Quando falta um, o compilador aponta o membro ausente (TS2322).
Como tratar vários cases com o mesmo código em um switch no TypeScript?
Empilhe os labels sem código entre eles: case "sat": case "sun": return "weekend";. Cases vazios passam direto para o próximo. A opção noFallthroughCasesInSwitch só aponta cases que têm código e não têm break nem return, então labels empilhados continuam permitidos.
switch (true) faz narrowing de tipos no TypeScript?
Sim, desde o TypeScript 5.3. Em switch (true) { case typeof x === "string": ... }, x é estreitado para string dentro daquele case, assim como seria em um if. É uma alternativa legível a uma sequência de if/else if com verificações de faixa ou de tipo.