switch case em JavaScript: como usar e quando usar
O switch compara com === estrito, então "1" não bate com case 1. Fall-through sem break, agrupamento de cases, o truque switch(true) e o erro do let solto.
switch compara uma expressão contra uma lista de valores fixos e executa o
bloco do primeiro que bater. É o formato que substitui a escada de else if
quando todas as perguntas são sobre a mesma variável.
Duas regras explicam praticamente todo bug de switch: a comparação é estrita
(===, sem conversão de tipo), e a execução não para sozinha no fim de um
case — quem para é o break. Quem entende essas duas linhas não erra mais.
Continuamos na loja: status de pedido, forma de pagamento, prazo de entrega. Se você chegou aqui direto, if else em JavaScript cobre a versão com escada.
Pense num atendente com uma tabela de status
Um atendente recebe o status enviado e percorre uma tabela: se for pago,
libera a separação; se for enviado, mostra o rastreio; se não reconhecer,
chama o responsável. O switch formaliza esse balcão. A expressão é a ficha
recebida, cada case é um valor conhecido e default é o caminho para aquilo
que não estava na tabela.
O detalhe que a analogia não mostra é o fall-through: sem break, o
JavaScript não volta ao balcão depois de encontrar uma linha; ele continua
lendo as linhas seguintes. Faça uma previsão antes do primeiro exemplo:
remova um break e anote quantas mensagens devem aparecer. Depois rode. É esse
experimento, e não a tradução da palavra switch, que fixa a regra.
Anatomia
const status = 'enviado';
switch (status) {
case 'aguardando':
console.log('Pedido aguardando pagamento');
break;
case 'pago':
console.log('Pagamento confirmado, separando no estoque');
break;
case 'enviado':
console.log('Pedido a caminho');
break;
default:
console.log(`Status desconhecido: ${status}`);
}O valor entre parênteses é avaliado uma vez. O motor percorre os case de cima
para baixo comparando com ===, entra no primeiro que bate e executa dali para
frente até encontrar break (ou o fim do switch). Nenhum case bateu?
Executa o default.
A comparação é estrita — e é aqui que dói
switch usa ===. Não existe conversão de tipo, e é isso que quebra todo
formulário que envia número como texto:
const idDaFormaDePagamento = '1';
switch (idDaFormaDePagamento) {
case 1:
console.log('Cartão de crédito');
break;
case 2:
console.log('Pix');
break;
default:
console.log('Forma de pagamento não reconhecida');
}
console.log(idDaFormaDePagamento == 1);
console.log(idDaFormaDePagamento === 1);A string '1' não bate com case 1. E repare no detalhe cruel: com ==
seria true. Um if (id == 1) que funcionava vira um switch que não funciona,
e a mudança parecia puramente cosmética.
De onde vem a string: input.value no navegador, req.query no Express,
req.params em rota, FormData.get(). Todos devolvem string, sempre, mesmo
quando o campo é type="number".
const MENSAGENS = {
aguardando: 'Pedido aguardando pagamento',
pago: 'Pagamento confirmado, separando no estoque',
enviado: 'Pedido a caminho',
entregue: 'Pedido entregue',
};
function mensagemDoStatus(status) {
return MENSAGENS[status] ?? `Status desconhecido: ${status}`;
}
console.log(mensagemDoStatus('pago'));
console.log(mensagemDoStatus('devolvido'));
console.log(mensagemDoStatus('toString'));Guardei este exemplo para logo depois da comparação estrita porque ele mostra o
outro lado da moeda. O objeto de consulta resolve o caso comum em uma linha —
mas a última saída denuncia o preço: MENSAGENS['toString'] devolveu uma função
herdada do prototype, e o ?? não pegou nada porque o valor não era null.
Com switch, 'toString' cairia no default sem drama. Se você preferir o
objeto — e ele costuma ser a melhor escolha — crie-o com
Object.create(null) ou verifique com Object.hasOwn(MENSAGENS, status).
Esquecer o break executa todos os cases seguintes
Este é o erro característico do switch, e ele não gera exceção nenhuma — só
saída errada:
const status = 'pago';
switch (status) {
case 'aguardando':
console.log('Pedido aguardando pagamento');
case 'pago':
console.log('Pagamento confirmado, separando no estoque');
case 'enviado':
console.log('Pedido a caminho');
case 'entregue':
console.log('Pedido entregue');
default:
console.log('Fim do processamento');
}O pedido está apenas pago, e o programa anunciou que ele foi enviado e
entregue. O switch entrou no case 'pago' — correto — e dali executou tudo
para baixo, inclusive o default.
Repare que o case 'aguardando', que vem antes, não rodou. O case é um
ponto de entrada, não um teste repetido: uma vez lá dentro, os rótulos seguintes
são só rótulos.
Fall-through de propósito: agrupar cases
Quando você quer o comportamento em cascata, ele vira a forma mais limpa de
agrupar valores. Vários case empilhados, um único bloco:
function prazoEmDias(uf) {
switch (uf) {
case 'SP':
case 'RJ':
case 'MG':
case 'ES':
return 2;
case 'PR':
case 'SC':
case 'RS':
return 4;
case 'AM':
case 'AC':
case 'RR':
return 12;
default:
return 7;
}
}
console.log(prazoEmDias('SP'), prazoEmDias('RS'), prazoEmDias('AC'), prazoEmDias('BA'));Aqui o return faz o papel do break: sai da função inteira, então não há como
vazar para o grupo seguinte. Dentro de função, prefira return a break —
o switch fica mais curto e o esquecimento deixa de ser possível.
function acaoDoStatus(status) {
switch (status) {
case 'pago':
return 'separar no estoque';
case 'enviado':
return 'notificar o cliente';
default:
return 'nenhuma ação';
}
}
console.log(acaoDoStatus('pago'));
console.log(acaoDoStatus('enviado'));
console.log(acaoDoStatus('cancelado'));O default não precisa ser o último
A busca pelo case correspondente é feita antes de qualquer coisa; o default
só entra se nenhum bater, esteja ele onde estiver:
const status = 'cancelado';
switch (status) {
default:
console.log(`Status "${status}" caiu no default`);
break;
case 'pago':
console.log('Pagamento confirmado');
break;
case 'enviado':
console.log('Pedido a caminho');
break;
}Funciona — e mesmo assim escreva o default no fim. Não é regra da linguagem,
é onde todo leitor procura. E cuidado: default no meio sem break vaza
para o case de baixo como qualquer outro bloco.
Cada case compartilha o mesmo escopo
O switch inteiro é um bloco. Declarar let ou const dentro de dois
case diferentes, sem chaves, colide:
const status = 'pago';
switch (status) {
case 'pago':
let mensagem = 'Pagamento confirmado';
console.log(mensagem);
break;
case 'enviado':
let mensagem = 'Pedido a caminho';
console.log(mensagem);
break;
}SyntaxError: Identifier ‘mensagem’ has already been declared at compileSourceTextModule (node:internal/modules/esm/utils:318:16) at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:90:18) at #translate (node:internal/modules/esm/loader:435:20)
Node.js v24.16.0
SyntaxError: nada rodou. A indentação sugere dois escopos, mas existe um só.
A solução é dar chaves ao case:
const status = 'pago';
switch (status) {
case 'pago': {
const mensagem = 'Pagamento confirmado';
console.log(mensagem);
break;
}
case 'enviado': {
const mensagem = 'Pedido a caminho';
console.log(mensagem);
break;
}
}Agora cada case tem o próprio bloco, e os dois mensagem são variáveis
distintas — o mesmo escopo de bloco que
let e const trouxeram para o resto da
linguagem.
switch (true): intervalos dentro de um switch
O switch compara valores, não faixas. Existe um truque conhecido para
contornar isso: em vez de passar a variável, passe true e escreva a condição
em cada case.
function faixaDeFrete(total) {
switch (true) {
case total >= 199:
return 'grátis';
case total >= 99:
return 'R$ 9,90';
case total >= 49:
return 'R$ 14,90';
default:
return 'R$ 24,90';
}
}
console.log(faixaDeFrete(250), faixaDeFrete(120), faixaDeFrete(60), faixaDeFrete(30));Funciona porque cada case agora é comparado — com === — contra true, e o
primeiro que resultar verdadeiro entra. Como toda cadeia de faixas, a ordem é
o algoritmo: inverta as três linhas e todo pedido cai na primeira faixa.
Minha opinião depois de ver isso em revisão algumas dezenas de vezes: o
switch (true) só se paga quando você já tem um switch de valores no mesmo
lugar e precisa acrescentar duas faixas. Para uma cadeia nova, três if com
return dizem a mesma coisa sem exigir que o leitor decifre o truque.
Quando usar switch
| a decisão é sobre | use |
|---|---|
| a mesma variável contra 3+ valores fixos | switch |
| a mesma variável contra 2 valores | if / ternário |
| variáveis diferentes em cada pergunta | if / else if |
| intervalos numéricos | if com return, ou tabela de faixas |
| um mapa de chave para texto, sem lógica | objeto com Object.hasOwn |
E três hábitos que evitam a maior parte dos bugs desta lição: break (ou
return) em todo case; um default que grita em vez de ficar calado —
throw new Error(...) num status desconhecido é melhor que seguir em frente; e
typeof no valor antes de culpar o switch.
A próxima lição da trilha de JavaScript sai das
decisões e entra na repetição, com o laço for. O
guia completo de JavaScript tem o mapa inteiro.
Perguntas frequentes
Por que meu switch com número vindo de input não entra em nenhum case?
O default precisa ser o último?
Quando switch é melhor que if else?
Posso usar switch com objeto ou array no case?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — switch — developer.mozilla.org
- MDN — Igualdade estrita (===) — developer.mozilla.org


