Como Criar Erros Personalizados em JavaScript
Capturar um erro é simples. Saber qual erro você capturou—e porquê—é onde as coisas ficam complicadas. Quando seu bloco catch trata um timeout de banco de dados da mesma forma que trata uma entrada de usuário inválida, a depuração se torna adivinhação.
Erros personalizados em JavaScript resolvem isso. Eles permitem criar tipos de erro estruturados e identificáveis que carregam contexto significativo através da sua aplicação. Este guia cobre a abordagem moderna usando recursos do ES2022+, incluindo Error.cause para encadeamento de erros e classes de erro JavaScript que funcionam de forma confiável em navegadores e runtimes atuais.
Pontos-Chave
- Estenda a classe nativa
Errorusando a sintaxeclass ... extends Errorpara erros personalizados limpos e sustentáveis - Passe tanto
messagequantooptionsparasuper()para preservar stack traces e habilitar encadeamento de erros - Use
Error.cause(ES2022+) para encapsular e preservar erros originais de fontes externas - Adicione campos estruturados como
statusCodeoufieldem vez de codificar contexto em strings de mensagem - Mantenha hierarquias de erro rasas—um erro base com alguns subtipos especializados cobre a maioria das necessidades
O Padrão Standard: Estendendo Error
O tratamento de erros moderno em JavaScript começa com a sintaxe de classe. Esqueça a manipulação de protótipo legada—class ... extends Error é a abordagem base:
class ValidationError extends Error {
constructor(message, options) {
super(message, options)
this.name = 'ValidationError'
}
}
A chamada super(message, options) é crítica. Ela passa tanto a mensagem quanto um objeto de opções para o construtor Error pai, que captura o stack trace automaticamente.
Definir this.name garante que seu erro se identifique corretamente em stack traces e logs. Sem isso, seu erro personalizado é exibido como um “Error” genérico.
Adicionando Campos Estruturados
Strings de mensagem simples são frágeis. Fazer parsing de “Invalid email: user@” para extrair contexto é propenso a erros. Em vez disso, adicione campos estruturados:
class HttpError extends Error {
constructor(message, statusCode, options) {
super(message, options)
this.name = 'HttpError'
this.statusCode = statusCode
}
}
throw new HttpError('Resource not found', 404)
Agora seu manipulador de erros pode verificar error.statusCode diretamente em vez de fazer parsing de strings. Este padrão escala de forma limpa—adicione details, code, ou quaisquer campos específicos do domínio que sua aplicação precise.
Encadeamento de Erros com Error.cause
Ao encapsular erros de fontes externas—bancos de dados, APIs, bibliotecas—você precisa preservar o erro original. A opção cause, introduzida no ES2022, lida com isso:
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`)
if (!response.ok) {
throw new HttpError('User fetch failed', response.status)
}
return response.json()
} catch (error) {
throw new HttpError('Unable to load user', 500, { cause: error })
}
}
O erro original permanece acessível via error.cause, preservando a cadeia completa de erros para depuração. Isso é suportado em navegadores modernos e Node.js—sem necessidade de polyfills.
Discover how at OpenReplay.com.
Construindo Hierarquias de Erro Pequenas
Hierarquias de erro em JavaScript funcionam melhor quando mantidas rasas. Um erro de aplicação base com alguns subtipos especializados cobre a maioria das necessidades:
class AppError extends Error {
constructor(message, options = {}) {
super(message, { cause: options.cause })
this.name = this.constructor.name
this.statusCode = options.statusCode ?? 500
}
}
class NotFoundError extends AppError {
constructor(message, options = {}) {
super(message, { ...options, statusCode: 404 })
}
}
class ValidationError extends AppError {
constructor(message, field, options = {}) {
super(message, { ...options, statusCode: 400 })
this.field = field
}
}
Usar this.constructor.name define automaticamente o nome do erro a partir da classe, reduzindo código repetitivo nas subclasses.
Discriminando Erros em Fluxos Assíncronos
Erros personalizados brilham em código assíncrono onde você precisa lidar com diferentes modos de falha:
try {
const user = await fetchUser(userId)
} catch (error) {
if (error instanceof NotFoundError) {
return { status: 404, body: { message: 'User not found' } }
}
if (error instanceof ValidationError) {
return { status: 400, body: { message: error.message, field: error.field } }
}
// Erro inesperado—registrar e retornar resposta genérica
console.error('Unexpected error:', error.cause ?? error)
return { status: 500, body: { message: 'Internal error' } }
}
A verificação instanceof funciona corretamente através da cadeia de herança. Você também pode discriminar verificando campos personalizados ou error.name ao lidar com erros serializados.
Para cenários envolvendo múltiplas falhas simultâneas, AggregateError fornece uma forma padrão de agrupar erros—útil para operações paralelas onde várias podem falhar independentemente.
Consideração sobre TypeScript
Se você está usando TypeScript, defina target: "ES2022" ou superior, ou inclua "ES2022" no seu array lib dentro do tsconfig.json. Isso garante tipagem adequada para a propriedade cause na interface ErrorOptions.
Conclusão
Erros personalizados em JavaScript construídos com class ... extends Error fornecem exceções estruturadas e identificáveis que carregam contexto através da sua aplicação. Passe message e options para super(), use cause para encadeamento de erros, e adicione campos específicos do domínio em vez de codificar informações em strings de mensagem. Mantenha hierarquias rasas, e seu tratamento de erros se torna previsível e depurável.
Perguntas Frequentes
Erros personalizados permitem distinguir entre diferentes tipos de falha programaticamente. Em vez de fazer parsing de mensagens de erro para determinar o que deu errado, você pode usar verificações instanceof ou inspecionar propriedades personalizadas como statusCode ou field. Isso torna o tratamento de erros mais confiável e seu código mais fácil de depurar e manter.
Error.cause foi introduzido no ES2022 e é suportado em todos os navegadores modernos e Node.js 16.9+. Para ambientes mais antigos, a opção cause é simplesmente ignorada—seu código ainda funcionará, mas a propriedade cause será undefined. Considere um polyfill apenas se você precisar suportar runtimes legados.
Mantenha-a rasa—tipicamente dois níveis no máximo. Uma única classe de erro de aplicação base com um punhado de subtipos especializados cobre a maioria dos casos de uso. Hierarquias profundas adicionam complexidade sem benefício proporcional e tornam a refatoração mais difícil conforme sua aplicação evolui.
Sim. Quando múltiplas operações podem falhar independentemente, encapsule falhas individuais em erros personalizados, depois agrupe-os usando AggregateError. Cada erro no array errors mantém seu tipo, então você ainda pode usar verificações instanceof ao processar os resultados.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.