Pregunta 1¿Cuál es el name de una excepción creada a partir de class ShippingError extends Error {}?
Lanzar excepciones — throw y errores personalizados
Lanza excepciones con throw new Error, crea tus propios tipos de error con extends Error y conserva el error original con cause.
Aunque la página de pedidos envíe una cantidad de 0, la función que calcula el subtotal hace la cuenta igualmente, y se acepta un pedido de 0 yenes. Mostrar un aviso tampoco detiene a quien llama: no se entera del problema y sigue adelante hasta guardar el pedido.
En este artículo verás throw new Error, que te permite lanzar una excepción tú mismo, y los errores personalizados, que son tipos de error que defines tú.
Detenerse ante un valor incorrecto — throw new Error
Imagina que no quieres que se calcule el subtotal si la cantidad es un decimal o un número negativo. Avisar con return null no basta: si quien llama se olvida de comprobarlo, sumar el envío con null + 500 da 500 y el cálculo sigue adelante.
Un objeto Error (un valor con name y message, pensado para lanzarse como excepción) se crea con new Error("mensaje"). Si se lo pasas a throw, la excepción se lanza en ese mismo punto; si la llamada se hizo dentro de try, ese valor llega como el error de catch.
function calcSubtotal(unitPrice, quantity) {
// Si no es un entero mayor o igual que 1, lanza una excepción aquí mismo
if (!Number.isInteger(quantity) || quantity < 1) {
// new Error por sí solo no detiene nada. Pásalo a throw para convertirlo en excepción
throw new Error(`La cantidad debe ser un entero mayor o igual que 1: ${quantity}`);
}
return unitPrice * quantity;
}
try {
console.log(calcSubtotal(1200, 3)); // 3600
console.log(calcSubtotal(1200, 0)); // lanza una excepción, así que no se muestra nada
console.log("Pedido confirmado"); // esta línea tampoco se ejecuta
} catch (error) {
console.log(error.name); // Error
console.log(error.message); // La cantidad debe ser un entero mayor o igual que 1: 0
}
Pon el if de validación al principio de la función, antes del cálculo. throw sale de la función en ese mismo punto, igual que return, así que el return unitPrice * quantity; que viene después no se ejecuta y quien llama nunca recibe un 0.
Crear un tipo para los errores de entrada — extends Error
Una excepción lanzada con new Error siempre tiene Error como name, sea cual sea la validación. Un código postal con un número de dígitos incorrecto y un artículo sin existencias llegan con el mismo nombre, así que el bloque catch no puede saber si se trata de un fallo que el usuario puede arreglar corrigiendo lo que escribió.
Si añades extends Error a una class (la sintaxis que define un tipo de objeto que se crea con new), creas un tipo que hereda el comportamiento de Error. Si escribes name = "ValidationError"; dentro de las llaves, las excepciones creadas a partir de este tipo tendrán esa cadena como name.
// Hereda de Error para crear un tipo para los errores de entrada
class ValidationError extends Error {
name = "ValidationError"; // pasa a ser el name de las excepciones creadas con new
}
function checkZipCode(zipCode) {
if (zipCode.length !== 5) {
throw new ValidationError(`El código postal debe tener 5 dígitos: ${zipCode}`);
}
return zipCode;
}
try {
checkZipCode("9021");
} catch (error) {
console.log(error.name); // ValidationError
console.log(error.message); // El código postal debe tener 5 dígitos: 9021
console.log(error instanceof Error); // true
}
- Tanto para una excepción creada con
new Error("mensaje")como para cualquier tipo de este recuadro,instanceof Errorestrue
SyntaxError/TypeError/RangeError— los tipos del artículo anterior
ValidationError— representa un error de entradanamees el"ValidationError"escrito dentro de la clase- instanceof ValidationError es true solo dentro de este recuadro
También puedes pasar una cadena a throw, pero una cadena no tiene ni name ni message, así que leer error.message en catch da undefined. La tabla de abajo muestra qué puedes leer en catch según el valor que se pase a throw.
| Valor pasado a throw | name que se lee en catch | instanceof Error |
|---|---|---|
| "Código postal no válido" (cadena) | undefined (una cadena no lo tiene) | false |
| new Error("Código postal no válido") | Error | true |
| ValidationError con name | ValidationError | true |
| ValidationError sin name | Error (el heredado) | true |
Comprobar primero tus propios tipos — el orden de instanceof
Un error de entrada lo puede arreglar el usuario, pero una excepción inesperada como un TypeError no desaparece por mucho que cambie lo que escribió. Quieres que catch muestre dos mensajes distintos, pero si escribes las comprobaciones en el orden equivocado, hasta los errores de entrada reciben el mensaje pensado para las excepciones inesperadas.
if y else if se comprueban de arriba abajo, y solo se ejecuta la primera rama que coincide. Un ValidationError también da true con instanceof Error, así que si instanceof Error va primero, un ValidationError también acaba en esa rama.
class ValidationError extends Error {
name = "ValidationError";
}
const error = new ValidationError("El código postal debe tener 5 dígitos");
// Si compruebas Error primero, un ValidationError también cae en esta rama
if (error instanceof Error) {
console.log("No se pudo guardar"); // No se pudo guardar
} else if (error instanceof ValidationError) {
console.log("Revisa los datos ingresados"); // aquí no llega
}
// Comprueba primero la subclase
if (error instanceof ValidationError) {
console.log("Revisa los datos ingresados"); // Revisa los datos ingresados
} else if (error instanceof Error) {
console.log("No se pudo guardar");
}
Los tipos integrados como TypeError también heredan de Error, así que la rama instanceof Error puesta al final los recoge a todos. Usa esa rama para mostrar otro mensaje en las excepciones que no se arreglan corrigiendo los datos ingresados.
Lanzar con la excepción original adjunta — cause y excepciones no capturadas
Imagina que se lanza un SyntaxError en una función que lee la respuesta de un servicio de pagos. Si te limitas a relanzarlo, su tipo no le dice a quien llama que el fallo ocurrió al procesar un pago. Pero si lo cambias por una excepción de un tipo nuevo, pierdes el dato de que originalmente era un SyntaxError.
cause (una opción que adjunta la excepción original a una nueva) va en el segundo argumento, como en new Error("mensaje", { cause: error }). Funciona igual con los tipos creados con extends Error, y quien capture la nueva excepción puede leer la original desde error.cause.
class PaymentError extends Error {
name = "PaymentError";
}
function readPayment(text) {
try {
return JSON.parse(text);
} catch (error) {
// Adjunta la excepción original como cause y lánzala como error de pago
throw new PaymentError("No se puede leer el resultado del pago", { cause: error });
}
}
try {
readPayment('{"orderId":"P-3107","amount":}');
} catch (error) {
console.log(error.name); // PaymentError
console.log(error.message); // No se puede leer el resultado del pago
console.log(error.cause.name); // SyntaxError
}
error.nameesPaymentErrorerror.messageesNo se puede leer el resultado del pago
error.cause.nameesSyntaxErrorerror.cause.messagees la descripción de JSON.parse, sin cambios
Si la cambias por una excepción nueva sin cause, error.cause es undefined y no puedes leer ni el tipo ni la descripción originales. Con cause, quien llama puede usar error.name para mostrar un mensaje de fallo en el pago y, aun así, registrar lo que hay en error.cause.
Una excepción que nadie captura detiene la ejecución
Si ningún catch recibe una excepción lanzada, esta se convierte en una excepción no capturada (una excepción que ningún catch gestiona), y la ejecución se detiene ahí mismo; ninguna de las líneas restantes se ejecuta. Si quitas el try externo del código de arriba, se muestra un error con el nombre del tipo y el mensaje, como PaymentError: No se puede leer el resultado del pago.
Verificación de conocimientos
Responde cada pregunta una a una.
Pregunta 2Si compruebas instanceof Error primero, ¿en qué rama entra una excepción ValidationError?
Pregunta 3Capturaste con catch (error) una excepción lanzada con cause. ¿Cómo lees el name de la excepción original?