A verificação de exaustividade é um seguro em tempo de compilação de que cada membro de uma união foi tratado. O núcleo é o truque do assertNever: no ramo default do switch, digite o parâmetro como never — se um membro for esquecido, ele ainda pode chegar lá, o parâmetro deixa de ser never e o compilador acusa erro imediatamente.
Grupo
Restrição e Asserções de Tipos
Tipo
Sintaxe
Modo strict
Não necessário
ℹ️ Info O editor transpila TypeScript para JavaScript e o executa (sem verificação de tipos); veja a Saída de cada exemplo para o resultado da verificação.
📝 Sintaxe
function assertNever(x: never): never {
throw new Error("Unhandled: " + JSON.stringify(x));
}
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "square": return shape.side ** 2;
default:
return assertNever(shape); // erro de compilação aqui se um ramo for esquecido
}
}
⚙️ Parâmetros
x: never
never é subtipo de todos os tipos e não tem habitantes — o argumento só satisfaz never quando todos os ramos foram tratados e o tipo restante é vazio
Retorno:assertNever retorna never (nunca retorna normalmente; lança erro em tempo de execução); o ganho em tempo de compilação é o erro "Argument of type 'X' is not assignable to parameter of type 'never'".
▶ Exemplo
Edite o código abaixo e clique em Testar para ver o resultado transpilado:
Saída:
3.14
9
(Saída de execução após a transpilação: todos os ramos estão presentes, então assertNever nunca é alcançado. Adicionar kind: "triangle" a Shape sem um ramo faz o tsc relatar "Argument of type '{ kind: "triangle"; ... }' is not assignable to parameter of type 'never'" na linha do default)
ℹ️ Info Como funciona: o TS remove os membros já tratados da união dentro de um switch. Quando todos são tratados, shape é never e pode ser atribuído ao parâmetro never; se faltar um, a atribuição falha, com o erro apontando para a linha do default.
💡 Dica O maior ganho aparece ao adicionar um novo membro à união: não é preciso buscar chamadas no código inteiro — o compilador lista todos os switch que agora estão sem um ramo.
⚠️ Atenção A exaustividade depende de uniões discriminadas com discriminadores literais. Fazer switch em um campo de tipo string ou usar uma cadeia de if-else impede o compilador de julgar a exaustividade.
P Não posso simplesmente escrever um switch completo sem assertNever?
R noImplicitReturns/strict detectam caminhos sem valor de retorno, mas não um caso logicamente não tratado (por exemplo, um ramo que silenciosamente retorna um valor padrão). O assertNever transforma "caso esquecido" em erro de compilação.
P A exaustividade funciona com cadeias de if-else?
R Sim — chame assertNever(x) no último else; o mecanismo é o mesmo. Ainda assim, o switch tem melhor legibilidade e funciona melhor com a análise de discriminadores, então prefira-o.
P O assertNever chega a ser executado em tempo de execução?
R Não, se o código for mantido corretamente — ele só roda no cenário de ramo esquecido que o compilador já impede. Manter o throw é defensivo: se os dados em tempo de execução contradisserem os tipos declarados (por exemplo, dados antigos em cache), o problema aparece cedo em vez de seguir silenciosamente pelo ramo errado.
Equipe Técnica Web-Tutorial
Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.