Back

Cómo Crear Errores Personalizados en JavaScript

Cómo Crear Errores Personalizados en JavaScript

Capturar un error es sencillo. Saber qué error capturaste—y por qué—es donde las cosas se complican. Cuando tu bloque catch maneja un timeout de base de datos de la misma manera que maneja una entrada de usuario inválida, la depuración se convierte en adivinanza.

Los errores personalizados de JavaScript resuelven esto. Te permiten crear tipos de error estructurados e identificables que transportan contexto significativo a través de tu aplicación. Esta guía cubre el enfoque moderno utilizando características de ES2022+, incluyendo Error.cause para encadenamiento de errores y clases de error de JavaScript que funcionan de manera confiable en navegadores y entornos de ejecución actuales.

Puntos Clave

  • Extiende la clase nativa Error usando la sintaxis class ... extends Error para errores personalizados limpios y mantenibles
  • Pasa tanto message como options a super() para preservar los stack traces y habilitar el encadenamiento de errores
  • Usa Error.cause (ES2022+) para envolver y preservar errores originales de fuentes externas
  • Agrega campos estructurados como statusCode o field en lugar de codificar el contexto en cadenas de mensaje
  • Mantén las jerarquías de errores poco profundas—un error base con algunos subtipos especializados cubre la mayoría de las necesidades

El Patrón Estándar: Extender Error

El manejo moderno de errores en JavaScript comienza con la sintaxis de clases. Olvida la manipulación de prototipos heredada—class ... extends Error es el enfoque base:

class ValidationError extends Error {
  constructor(message, options) {
    super(message, options)
    this.name = 'ValidationError'
  }
}

La llamada a super(message, options) es crítica. Pasa tanto el mensaje como un objeto de opciones al constructor padre Error, que maneja la captura del stack trace automáticamente.

Establecer this.name asegura que tu error se identifique correctamente en los stack traces y registros. Sin esto, tu error personalizado se muestra como un “Error” genérico.

Agregar Campos Estructurados

Las cadenas de mensaje simples son frágiles. Analizar “Invalid email: user@” para extraer contexto es propenso a errores. En su lugar, agrega campos estructurados:

class HttpError extends Error {
  constructor(message, statusCode, options) {
    super(message, options)
    this.name = 'HttpError'
    this.statusCode = statusCode
  }
}

throw new HttpError('Resource not found', 404)

Ahora tu manejador de errores puede verificar error.statusCode directamente en lugar de analizar cadenas. Este patrón escala limpiamente—agrega details, code, o cualquier campo específico del dominio que tu aplicación necesite.

Encadenamiento de Errores con Error.cause

Al envolver errores de fuentes externas—bases de datos, APIs, bibliotecas—necesitas preservar el error original. La opción cause, introducida en ES2022, maneja esto:

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 })
  }
}

El error original permanece accesible a través de error.cause, preservando la cadena completa de errores para depuración. Esto es compatible con navegadores modernos y Node.js—no se necesitan polyfills.

Construir Jerarquías de Errores Pequeñas

Las jerarquías de errores de JavaScript funcionan mejor cuando se mantienen poco profundas. Un error de aplicación base con algunos subtipos especializados cubre la mayoría de las necesidades:

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 establece automáticamente el nombre del error desde la clase, reduciendo código repetitivo en las subclases.

Discriminar Errores en Flujos Asíncronos

Los errores personalizados brillan en código asíncrono donde necesitas manejar diferentes modos de fallo:

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 } }
  }
  // Error inesperado—registrar y devolver respuesta genérica
  console.error('Unexpected error:', error.cause ?? error)
  return { status: 500, body: { message: 'Internal error' } }
}

La verificación con instanceof funciona correctamente a través de la cadena de herencia. También puedes discriminar verificando campos personalizados o error.name cuando trabajas con errores serializados.

Para escenarios que involucran múltiples fallos simultáneos, AggregateError proporciona una forma estándar de agrupar errores—útil para operaciones paralelas donde varios pueden fallar independientemente.

Consideración para TypeScript

Si estás usando TypeScript, establece target: "ES2022" o superior, o incluye "ES2022" en tu array lib dentro de tsconfig.json. Esto asegura el tipado adecuado para la propiedad cause en la interfaz ErrorOptions.

Conclusión

Los errores personalizados de JavaScript construidos con class ... extends Error te brindan excepciones estructuradas e identificables que transportan contexto a través de tu aplicación. Pasa message y options a super(), usa cause para encadenamiento de errores, y agrega campos específicos del dominio en lugar de codificar información en cadenas de mensaje. Mantén las jerarquías poco profundas, y tu manejo de errores se volverá predecible y depurable.

Preguntas Frecuentes

Los errores personalizados te permiten distinguir entre diferentes tipos de fallos de manera programática. En lugar de analizar mensajes de error para determinar qué salió mal, puedes usar verificaciones instanceof o inspeccionar propiedades personalizadas como statusCode o field. Esto hace que el manejo de errores sea más confiable y tu código más fácil de depurar y mantener.

Error.cause fue introducido en ES2022 y es compatible con todos los navegadores modernos y Node.js 16.9+. Para entornos más antiguos, la opción cause simplemente se ignora—tu código seguirá funcionando, pero la propiedad cause será undefined. Considera un polyfill solo si debes soportar entornos de ejecución heredados.

Mantenla poco profunda—típicamente dos niveles como máximo. Una única clase de error de aplicación base con un puñado de subtipos especializados cubre la mayoría de los casos de uso. Las jerarquías profundas agregan complejidad sin beneficio proporcional y hacen que la refactorización sea más difícil a medida que tu aplicación evoluciona.

Sí. Cuando múltiples operaciones pueden fallar independientemente, envuelve los fallos individuales en errores personalizados, luego agrúpalos usando AggregateError. Cada error en el array errors retiene su tipo, por lo que aún puedes usar verificaciones instanceof al procesar los 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