TypeScript usa try, catch, finally y throw de JavaScript. La parte específica de TypeScript es la variable de catch: con strict activado, tiene tipo unknown, así que compruebas qué se lanzó antes de usarla.
La parte de ejecución (cómo se deshace la pila, qué tipos de error integrados existen) se explica en try/catch en JavaScript.
Por qué la variable de catch es unknown
JavaScript puede lanzar cualquier cosa: un Error, un string, un número, undefined, un objeto de una librería. TypeScript no puede saber cuál, así que con strict (la opción useUnknownInCatchVariables) la variable es unknown y debes estrecharla antes de usarla:
index.ts(5,34): error TS18046: 'e' is of type 'unknown'.
Sin strict, e es any y el mismo código compila, pero falla en tiempo de ejecución cuando se lanza un string: e.message es undefined, y cualquier cosa más profunda, como e.message.length, lanza un TypeError. Tampoco puedes arreglarlo con una anotación: catch (e: Error) es el error TS1196 (Catch clause variable type annotation must be 'any' or 'unknown' if specified).
Estrechar el error
instanceof Error cubre todo lo que se basa en Error, incluidos TypeError, SyntaxError, RangeError y tus propias subclases. Para cualquier otra cosa, recurre a convertir el valor en un string. Una pequeña función auxiliar mantiene cortas las llamadas:
Un catch que solo maneja algunos errores debe volver a lanzar el resto: if (!(e instanceof ValidationError)) throw e;. Tragarse errores desconocidos oculta bugs reales.
Lanzar errores
throw acepta cualquier expresión, y TypeScript no lo restringe. Aun así, lanza objetos Error: llevan una traza de la pila, y todas las comprobaciones instanceof Error del código dependen de ello.
Una función que siempre lanza un error tiene el tipo de retorno never, que TypeScript usa para estrechar después de la llamada:
Desde ES2022, Error admite un segundo argumento con un cause, que encadena un error de bajo nivel al que lanzas: throw new Error("could not load settings", { cause: e }). Quien llama puede leer err.cause (de tipo unknown).
Clases de error propias
Una subclase de Error permite a quien llama distinguir los fallos con instanceof y llevar datos extra. Asigna name, porque el heredado es "Error" y aparece en los logs y en String(err):
Comprueba primero la clase más específica, ya que un NotFoundError también es un HttpError. El modificador override es opcional aquí salvo que noImplicitOverride esté activado.
Las guías antiguas añaden Object.setPrototypeOf(this, new.target.prototype) en cada constructor de error. Eso hacía falta al compilar las clases a funciones de ES5, donde instanceof fallaba con las subclases de Error. TypeScript 7 ya no admite target: "es5" (informa TS5108: Option 'target=ES5' has been removed), y con ES2015 o posterior el class ... extends Error nativo funciona, como arriba.
El patrón Result
TypeScript no registra qué errores puede lanzar una función, así que nada le recuerda a quien llama que debe manejarlos. Para los fallos esperados (una entrada inválida, un registro que no existe), devolver un valor que diga «éxito o fallo» mete el error en el sistema de tipos:
Leer r.value antes de comprobar r.ok es un error de compilación, y esa es la idea. Usa excepciones para lo inesperado (bugs, una conexión caída) y valores Result para los resultados sobre los que quien llama debe decidir.
Errores en código asíncrono
Una promesa rechazada se convierte en un error lanzado en el await, así que un try/catch alrededor de await funciona igual, y la variable vuelve a ser unknown. La única diferencia es .catch() sobre una promesa: el parámetro de su callback tiene tipo any, no unknown, así que anótalo tú (.catch((e: unknown) => ...)). Consulta async/await para ver ejemplos.
Errores habituales
- Leer
e.messagesin estrechar. Constrictno compila; sinstrictfalla cuando se lanza algo que no es unError. - Capturarlo todo y seguir. Maneja los errores que esperas y vuelve a lanzar el resto.
- Olvidar
nameen los errores propios. Entonces los logs dicenErrorpara todos. - Lanzar strings.
throw "failed"no tiene traza de la pila y no pasa las comprobacionesinstanceof Error.
Preguntas frecuentes
¿De qué tipo es el error en un bloque catch de TypeScript?
unknown cuando strict está activado (la opción useUnknownInCatchVariables), y any en caso contrario. JavaScript puede lanzar cualquier valor, no solo objetos Error, así que TypeScript te obliga a comprobarlo. Estréchalo con if (e instanceof Error) antes de leer e.message.
¿Puedo tipar la variable de catch como Error en TypeScript?
No. catch (e: Error) es el error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. Solo se permiten unknown y any, porque nada garantiza qué se lanzó. Estrecha dentro del bloque.
¿Cómo lanzo un error en TypeScript?
Igual que en JavaScript: throw new Error("message"), o una instancia de una subclase integrada o propia como new RangeError(...). TypeScript te deja lanzar cualquier valor, pero lanzar objetos Error conserva la traza de la pila y hace que funcionen las comprobaciones instanceof Error.
¿Cómo creo una clase de error propia en TypeScript?
Extiende Error, llama a super(message, options) y asigna name: class NotFoundError extends Error { name = "NotFoundError"; }. Los campos extra van en el constructor. Con cualquier target admitido (ES2015 y posteriores), instanceof NotFoundError funciona sin arreglar el prototipo.
¿TypeScript tiene excepciones comprobadas o una cláusula throws?
No. El tipo de una función no dice nada de lo que puede lanzar, y el compilador nunca comprueba que se manejen los errores. Para los errores que se espera que maneje quien llama, devuélvelos como valores con un tipo Result (una unión discriminada de éxito y fallo) para que el sistema de tipos sí lo compruebe.