Back

Como Criar Erros Personalizados em JavaScript

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 Error usando a sintaxe class ... extends Error para erros personalizados limpos e sustentáveis
  • Passe tanto message quanto options para super() 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 statusCode ou field em 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.

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.

OpenReplay