Menu

Try catch em TypeScript: tipos de erro e erros personalizados

Tratamento de erros em TypeScript: por que a variável do catch é unknown, como estreitá-la com instanceof Error, como lançar erros, como escrever classes de erro personalizadas com name e cause, e o padrão Result para erros que você espera.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

O TypeScript usa o try, catch, finally e throw do JavaScript. A parte específica do TypeScript é a variável do catch: com strict ligado, ela tem o tipo unknown, então você verifica o que foi lançado antes de usá-la.

O lado de tempo de execução (como a pilha é desfeita, quais tipos de erro embutidos existem) é coberto em try/catch em JavaScript.

Por que a variável do catch é unknown

O JavaScript pode lançar qualquer coisa: um Error, uma string, um número, undefined, um objeto de uma biblioteca. O TypeScript não tem como saber qual, então com strict (a flag useUnknownInCatchVariables) a variável é unknown e você precisa estreitá-la antes de usar:

index.ts(5,34): error TS18046: 'e' is of type 'unknown'.

Sem strict, e é any e o mesmo código compila, mas dá errado em tempo de execução quando uma string é lançada: e.message é undefined, e qualquer coisa mais profunda, como e.message.length, lança um TypeError. Também não dá para resolver com uma anotação: catch (e: Error) dá o erro TS1196 (Catch clause variable type annotation must be 'any' or 'unknown' if specified).

Estreitando o erro

instanceof Error cobre tudo o que é construído sobre Error, incluindo TypeError, SyntaxError, RangeError e as suas próprias subclasses. Para qualquer outra coisa, converta o valor para string como alternativa. Um pequeno helper deixa os pontos de chamada curtos:

Um catch que trata só alguns erros deve relançar o resto: if (!(e instanceof ValidationError)) throw e;. Engolir erros desconhecidos esconde bugs reais.

Lançando erros

throw aceita qualquer expressão, e o TypeScript não a restringe. Lance objetos Error mesmo assim: eles carregam um stack trace, e toda verificação instanceof Error do código depende disso.

Uma função que sempre lança um erro tem o tipo de retorno never, que o TypeScript usa para estreitar depois da chamada:

Desde o ES2022, Error recebe um segundo argumento com um cause, que encadeia um erro de baixo nível ao que você lança: throw new Error("could not load settings", { cause: e }). Quem chama pode ler err.cause (tipado como unknown).

Classes de erro personalizadas

Uma subclasse de Error permite que quem chama diferencie falhas com instanceof e carregue dados extras. Defina name, porque o herdado é "Error" e ele aparece nos logs e em String(err):

Verifique primeiro a classe mais específica, já que um NotFoundError também é um HttpError. O modificador override é opcional aqui, a menos que noImplicitOverride esteja ligado.

Guias mais antigos adicionam Object.setPrototypeOf(this, new.target.prototype) a todo construtor de erro. Isso era necessário ao compilar classes para funções ES5, em que instanceof quebrava para subclasses de Error. O TypeScript 7 não suporta mais target: "es5" (ele reporta TS5108: Option 'target=ES5' has been removed), e com ES2015 ou posterior o class ... extends Error nativo funciona, como acima.

O padrão Result

O TypeScript não rastreia quais erros uma função pode lançar, então nada lembra quem chama de tratá-los. Para falhas esperadas (entrada inválida, um registro inexistente), retornar um valor que diz "sucesso ou falha" coloca o erro dentro do sistema de tipos:

Ler r.value antes de verificar r.ok é erro de compilação, e essa é a ideia. Use exceções para o inesperado (bugs, uma conexão caída) e valores Result para desfechos sobre os quais quem chama precisa decidir.

Erros em código assíncrono

Uma promise rejeitada vira um erro lançado no await, então try/catch em volta de await funciona do mesmo jeito, e a variável é de novo unknown. A única diferença é o .catch() de uma promise: o parâmetro do callback dele é tipado como any, não unknown, então anote-o você mesmo (.catch((e: unknown) => ...)). Veja async/await para exemplos.

Erros comuns

  • Ler e.message sem estreitar. Com strict não compila; sem strict, quebra quando algo que não é Error é lançado.
  • Capturar tudo e seguir em frente. Trate os erros que você espera e relance o resto.
  • Esquecer name nos erros personalizados. Aí os logs dizem Error para todos eles.
  • Lançar strings. throw "failed" não tem stack trace e falha nas verificações instanceof Error.

Perguntas frequentes

Qual é o tipo do erro em um bloco catch do TypeScript?

unknown quando strict está ligado (a flag useUnknownInCatchVariables), e any caso contrário. O JavaScript pode lançar qualquer valor, não só objetos Error, então o TypeScript obriga você a verificar. Estreite-o com if (e instanceof Error) antes de ler e.message.

Posso tipar a variável do catch como Error no TypeScript?

Não. catch (e: Error) dá o erro TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. Só unknown e any são permitidos, porque nada garante o que foi lançado. Faça o estreitamento dentro do bloco.

Como lançar um erro no TypeScript?

Do mesmo jeito que no JavaScript: throw new Error("message"), ou uma instância de uma subclasse embutida ou personalizada, como new RangeError(...). O TypeScript deixa você lançar qualquer valor, mas lançar objetos Error preserva o stack trace e faz as verificações instanceof Error funcionarem.

Como criar uma classe de erro personalizada no TypeScript?

Estenda Error, chame super(message, options) e defina name: class NotFoundError extends Error { name = "NotFoundError"; }. Campos extras vão no construtor. Com qualquer target suportado (ES2015 em diante), instanceof NotFoundError funciona sem correção de prototype.

O TypeScript tem checked exceptions ou uma cláusula throws?

Não. O tipo de uma função não diz nada sobre o que ela pode lançar, e o compilador nunca verifica se os erros são tratados. Para erros que quem chama deve tratar, retorne-os como valores com um tipo Result (uma discriminated union de sucesso e falha), para que o sistema de tipos faça a verificação.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR