DanLevy.net

Quiz: Dominio Avanzado de Errores en JS

¿Tus excepciones son realmente excepcionales?

¿Crees que dominas los errores de JavaScript al detalle?

¿Qué devuelve JSON.stringify(error)?

const error = new Error('Oops');
console.log(JSON.stringify(error));

Los objetos Error tienen propiedades no enumerables (message, name, stack), por lo que JSON.stringify() devuelve {}. Esto es una trampa frecuente al enviar errores en respuestas de API. Usa JSON.stringify(error, Object.getOwnPropertyNames(error)) o crea un objeto plano en su lugar.

Piensa en cuáles de las propiedades de Error son enumerables.

¿Cuál es la diferencia entre estos dos?

const err = new Error('Test');
console.log(err);
console.log(JSON.stringify(err));

console.log(err) muestra el error con su mensaje y traza de pila porque la consola tiene un manejo especial para objetos Error. JSON.stringify(err) devuelve '{}' porque las propiedades de Error no son enumerables. Esta diferencia confunde a muchos desarrolladores al depurar APIs.

La consola puede mostrar detalles de un objeto que no se incluyen en su serialización JSON.

¿Cuáles son los resultados de estas comprobaciones?

class CustomError extends Error {}
const err = new CustomError('test');
console.log(err instanceof CustomError);
console.log(err instanceof Error);
console.log(err instanceof Object);

Los tres devuelven true. CustomError extiende a Error, que a su vez extiende a Object. El operador instanceof verifica toda la cadena de prototipos, por lo que una instancia de CustomError también es una instancia de Error y Object.

Recorre la cadena de prototipos de CustomError, Error y Object.

Supón que un iframe del mismo origen crea un objeto Error y lo pasa a la ventana principal como iframeError. La siguiente comprobación se ejecuta en la ventana principal. ¿Qué puede ocurrir?

console.log(iframeError instanceof Error);

Cada contexto de ejecución tiene su propio constructor Error, así que instanceof Error puede devolver false para un error creado en otro iframe. Object.prototype.toString.call(obj) puede reconocer su etiqueta entre contextos, pero Symbol.toStringTag permite falsificarla; no es una comprobación de autenticidad para objetos no confiables.

Cada contexto de ejecución tiene su propio constructor Error.

¿Qué ocurre cuando lanzas una cadena?

try {
throw "Oops!";
} catch (e) {
console.log(e instanceof Error);
console.log(typeof e);
}

JavaScript permite lanzar cualquier valor. Aquí, e instanceof Error es false y typeof e es "string". Esto puede romper el código de manejo de errores que asume que todas las excepciones capturadas son objetos Error. Siempre lanza instancias de Error para una mejor depuración.

JavaScript permite lanzar cualquier valor. Lanzar una cadena no la convierte en una instancia de Error.

¿Cuál es el valor de err.name?

class CustomError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
const err = new CustomError('test');
console.log(err.name);

err.name es "CustomError" porque this.constructor.name devuelve el nombre de la clase. Asignar this.name = this.constructor.name es un patrón común para garantizar que las clases de error personalizadas muestren el nombre correcto en trazas de pila y mensajes de error.

Comprueba qué nombre devuelve this.constructor.name en esta instancia.

¿Cuál es la salida sin establecer name?

class MyError extends Error {
// No constructor or name setting
}
const err = new MyError('test');
console.log(err.name);

Sin establecer explícitamente this.name, el error hereda la propiedad name predeterminada de la clase Error, que es "Error". Por eso las clases de error personalizadas siempre deben establecer this.name = this.constructor.name en su constructor.

Sin una asignación propia, el nombre se hereda del prototipo de Error.

¿Qué devuelve wrapper.cause.message?

const original = new Error('Original error');
const wrapper = new Error('Wrapper',
{ cause: original }
);
console.log(wrapper.cause.message);

Error.cause (ES2022) permite encadenar errores para preservar el contexto del error original. wrapper.cause hace referencia al error original, por lo que wrapper.cause.message devuelve "Original error". Esto es útil para envolver errores de bajo nivel con un contexto de nivel superior.

cause conserva la referencia al error original; sigue esa referencia hasta su propiedad message.

En V8/Node.js, ¿qué hace Error.captureStackTrace?

function createError(msg) {
const err = new Error(msg);
Error.captureStackTrace(err, createError);
return err;
}
const error = createError('test');

Error.captureStackTrace (V8/Node.js) elimina la función especificada (createError) del rastro de pila, haciendo que las funciones fábrica de errores sean invisibles para los usuarios finales. Esto genera rastros de pila más limpios que apuntan al lugar donde se llamó a la fábrica, no a la propia fábrica.

captureStackTrace es una función específica de V8 que permite omitir marcos al crear la traza.

¿Cuál es el mensaje de error?

function validate(value) {
if (!value) {
throw new Error(
`Value ${value} is invalid`
);
}
}
try {
validate(undefined);
} catch (e) {
console.log(e.message);
}

Las plantillas de texto convierten undefined a la cadena "undefined" durante la interpolación. El mensaje de error se vuelve "Value undefined is invalid". Para mensajes más claros, considera usar value ?? 'null' o verificaciones similares antes de la interpolación.

La interpolación de una plantilla convierte undefined en texto; no deja un espacio vacío.

¿Qué se envía al cliente?

// Express.js route
app.get('/api/data', (req, res) => {
const error = new Error('Database failed');
res.json({ error });
});

res.json() usa internamente JSON.stringify(), por lo que el objeto Error se convierte en {}. El cliente recibe {"error":{}}. Para solucionarlo, usa res.json({ error: error.message }) o res.json({ error: { message: error.message, name: error.name } }).

Recuerda qué propiedades de Error incluye JSON.stringify al serializar el objeto.

¿Qué puede aceptar Promise.reject()?

Promise.reject('string').catch(e =>
console.log(typeof e)
);
Promise.reject({code: 404}).catch(e =>
console.log(e.code)
);
Promise.reject(42).catch(e =>
console.log(e)
);

Al igual que throw, Promise.reject() acepta cualquier valor: cadenas, objetos, números, etc. Esto imprime "string", 404 y 42. Siempre verifica el tipo de los valores capturados en las cadenas de promesas, sobre todo al trabajar con código de terceros que podría rechazar con valores que no sean Error.

Como throw, un rechazo de promesa puede transportar valores de distintos tipos.

¿Qué tan fiables son error.code y error.errno?

const fs = require('fs');
fs.readFile('missing.txt', (err, data) => {
if (err) {
console.log(err.code); // 'ENOENT'
console.log(err.errno); // -2
}
});

Propiedades como code y errno son específicas del entorno (Node.js en este caso) y no forman parte del objeto Error estándar. Los errores del navegador no tendrán estas propiedades. Siempre verifica su existencia: if (err.code === 'ENOENT') en lugar de asumir que existen.

Las propiedades de los errores del sistema operativo no están disponibles en todos los entornos JavaScript.

¿Qué devuelven estas comprobaciones?

const fakeError = {
name: 'Error',
message: 'Fake error',
stack: 'fake stack'
};
console.log(fakeError instanceof Error);
console.log(Object.prototype.toString.call(
fakeError
) === '[object Error]');

Ambas comparaciones devuelven false: la cadena de prototipos del objeto no incluye Error.prototype, y Object.prototype.toString.call(fakeError) devuelve '[object Object]'. Eso describe este objeto concreto; un objeto puede falsificar la etiqueta con Symbol.toStringTag, así que la etiqueta no demuestra que sea un Error auténtico.

Una comprobación recorre la cadena de prototipos; la otra devuelve una etiqueta que Symbol.toStringTag puede modificar.

Domina el Arte del Manejo de Errores

Desde los escollos de la serialización hasta los fallos de instanceof entre contextos, estos conceptos avanzados son lo que separa a los desarrolladores junior de los profesionales experimentados.

¿Listo para más desafíos? Consulta nuestra colección completa de quizzes para encontrar más acertijos sobre JavaScript, algoritmos y mucho más.