El manejo de errores es uno de los grandes faltantes en la mayoría de tutoriales disponibles en la web. Muchas veces, simplemente lanzar instancias de Error no es suficiente para poder lidiar con los diferentes tipos de Error que puede emitir una clase o una función.
Esto es aun peor cuando se consumen APIs externas, que pueden emitir muchos tipos diferentes de errores, que en cada caso habrá que manejar apropiadamente y de manera diferente.
El primer paso para lograrlo es extender la clase Error, para que pueda ser un poco más descriptiva:
export class Failure extends Error { public code = ''; constructor(message: string, code: string | null = null) { super(message); if (code) { this.code = code; } Object.setPrototypeOf(this, new.target.prototype); } }
Ahora contamos con una clase Failure, que extiende Error y que adicional nos permite tener un code, que en algunos casos es muy útil. Ahora es solo cuestión de extender dicha clase para cada uno nuestros errores:
export class NotFoundFailure extends Failure {} export class UnknownFailure extends Failure {}
Así, en nuestro código podemos lanzar errores específicos y documentar apropiadamente nuestras funciones, indicando que también devuelven ‘never’, es decir, que podrían emitir un error. El ejemplo a continuación intenta ilustrarlo. Usen su imaginación!:
export class CosaClient { // ... /** * Devuelve una Cosa. * ... * * @throws NotFoundFailure | UnknownFailure */ fetchId(id: string): Promise<Cosa | never> { // ... // Tirar un error! if (...) { throw new NotFoundFailure('Mensaje', '123'); // ... // Tirar otro error! if (...) { throw new UnknownFailure('Mensaje', '123'); } // ... } }
Luego, cuando hagamos uso de la clase CosaClient, podemos saber por la documentación que emite determinados errores y hacer el manejo apropiado de ellos:
try { // Instanciar la clase. const cosaClient = new CosaClient(); // Recuperar el ID 123. const cosa = CosaClient.fetchId('123'); // ... // Manejar los errores que puedan ocurrir en el catch. } catch (e) { if (e instanceof NotFoundFailure) { // Decirle al usuario que ese ID no existe. } else if (e instanceof UnknownFailure) { // Decirle al usuario que ocurrió un error inesperado. } else if (e instanceof Error) { // Manejar cualquier error genérico que venga de otra parte. } else { // Manejar cualquier otra cosa inesperada. } }
Finalmente, cómo nuestros propios Failures cuentan con campos para un mensaje y un código de error, podremos tener mensajes más claros y hacer un mejor manejo con las variantes de cada error.
¡Espero que esto les de ideas de como lidiar mejor con sus Errores!