Historia de la pregunta: En JavaScript, los errores estándar se describen a través de la clase Error. En grandes proyectos de TypeScript, es importante utilizar su propia jerarquía de errores para un manejo preciso de situaciones, pero hay matices con tipos, herencia y el comportamiento de 'instanceof'.
Problema: Al heredar de Error pueden surgir dificultades: se pierde el prototipo, 'instanceof' no funciona correctamente, los tipos pueden estar mal descritos y la serialización a menudo conduce a la pérdida de stack trace. Sin una especificación clara de las propiedades, se pueden obtener errores al manejar las excepciones.
Solución: Definir correctamente los errores personalizados ampliando la clase Error. Se debe especificar explícitamente el nombre, restaurar el prototipo manualmente (importante para ES5 y al compilar a CommonJS), y tipificar los campos de los errores.
Ejemplo de código:
class ValidationError extends Error { code: number; constructor(message: string, code: number = 400) { super(message); Object.setPrototypeOf(this, ValidationError.prototype); this.name = 'ValidationError'; this.code = code; } } function process(user: string) { if (!user) throw new ValidationError('User required', 401); }
Características clave:
¿Por qué no se puede omitir la llamada a Object.setPrototypeOf(this, ...)?
Si no se llama a Object.setPrototypeOf(this, Class.prototype), 'instanceof' ValidationError no funcionará al compilar a ES5/CommonJS o Babel. Esto hará que los bloques catch de ValidationError no intercepten el error.
class CustomErr extends Error {} const err = new CustomErr('msg'); console.log(err instanceof CustomErr); // false sin setPrototypeOf
¿Se puede omitir el campo name en un error personalizado?
Si no se establece la propiedad this.name, el stack del error y la visualización en los logs serán incorrectos, lo que complicará la búsqueda de la causa y la clasificación de los errores.
¿Es necesario hacer que los errores sean serializables, y si es así, cómo?
Los errores deben poder serializarse correctamente (por ejemplo, para logging o transmisión en red), de lo contrario, JSON.stringify(new Error()) no devolverá message y stack. Se debe sobreescribir el método toJSON.
class SerializableError extends Error { toJSON() { return { name: this.name, message: this.message, stack: this.stack }; } }
En el proyecto se hizo simplemente class MyError extends Error, sin restaurar el prototipo. Los errores se capturaban mediante if (err instanceof MyError), pero esto no funcionaba, y el código silenciosamente pasaba por alto el manejo de situaciones críticas.
Pros:
Contras:
Se implementó correctamente CustomError, especificando claramente el name, code y toJSON, y se cubrieron las pruebas para el manejo de diferentes tipos de errores. En los logs y manejadores catch, la estructura de los errores se volvió clara, lo que redujo el tiempo de búsqueda de bugs.
Pros:
Contras: