Manejo de errores en TypeScript: del try/catch básico a patrones industriales
\n\nEl manejo de errores no son solo bloques try/catch. En TypeScript, gracias a su estricto sistema de tipos, podemos llevar el control de errores a un nivel completamente nuevo. En este artículo, analizaremos técnicas avanzadas que te ayudarán a escribir código más robusto y predecible.
El problema del try/catch estándar en TypeScript
\n\nEl bloque clásico try/catch en TypeScript tiene un problema fundamental: el tipo del error capturado es unknown (o any en versiones antiguas). Esto significa que TypeScript no sabe con qué objeto exacto estamos tratando.
try {\n // algún código peligroso\n const result = JSON.parse('{"data": 123}');\n} catch (error) {\n // error tiene tipo unknown\n // No se puede simplemente llamar a error.message\n console.log(error.message); // ¡Error de compilación!\n}\n\nEsto obliga a los desarrolladores a ignorar los tipos o escribir comprobaciones engorrosas. Veamos cómo resolver este problema a un nivel avanzado.
\n\n1. Creación de un sistema de errores personalizados con tipado
\n\nEn lugar de lanzar Error comunes, crea una jerarquía de errores personalizados. Esto permitirá que TypeScript distinga los tipos de error en tiempo de compilación.
// Clase base para todos los errores de la aplicación\nclass AppError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly statusCode: number = 500\n ) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\n// Tipos de error específicos\nclass ValidationError extends AppError {\n constructor(message: string) {\n super(message, 'VALIDATION_ERROR', 400);\n }\n}\n\nclass NotFoundError extends AppError {\n constructor(resource: string) {\n super(`${resource} no encontrado`, 'NOT_FOUND', 404);\n }\n}\n\nclass DatabaseError extends AppError {\n constructor(message: string) {\n super(message, 'DB_ERROR', 500);\n }\n}\n\n// Uso\nfunction findUser(id: string): User {\n if (!id) throw new ValidationError('ID es obligatorio');\n // ... lógica de búsqueda\n if (!user) throw new NotFoundError('Usuario');\n return user;\n}\n\n2. Type guards para el manejo de errores
\n\nPara manejar de forma segura diferentes tipos de errores, utiliza type guards. Este es un patrón clave para trabajar con unknown en catch.
// Type guard para AppError\nfunction isAppError(error: unknown): error is AppError {\n return error instanceof AppError;\n}\n\n// Type guard para ValidationError\nfunction isValidationError(error: unknown): error is ValidationError {\n return error instanceof ValidationError;\n}\n\n// Manejo avanzado\ntry {\n const user = findUser(userId);\n} catch (error) {\n if (isValidationError(error)) {\n // TypeScript sabe que error es ValidationError\n console.error(`Error de validación: ${error.message}`);\n return { status: 400, message: error.message };\n }\n \n if (isAppError(error)) {\n // error es AppError\n console.error(`Código de error: ${error.code}`);\n return { status: error.statusCode, message: error.message };\n }\n \n // Error desconocido\n if (error instanceof Error) {\n console.error('Error desconocido:', error.message);\n }\n \n return { status: 500, message: 'Error interno del servidor' };\n}\n\n3. Patrón Result (Mónada Either)
\n\nEsta es una alternativa a las excepciones que hace explícito el manejo de errores a nivel de tipos. En lugar de lanzar un error, la función devuelve un objeto que puede contener un resultado exitoso o un error.
\n\n// Definición de tipos para el patrón Result\ntype Success<T> = { success: true; data: T };\ntype Failure<E> = { success: false; error: E };\ntype Result<T, E> = Success<T> | Failure<E>;\n\n// Funciones auxiliares\nfunction success<T>(data: