Fehlerbehandlung in TypeScript: Vom einfachen try/catch zu industriellen Patterns
\n\nFehlerbehandlung ist nicht nur try/catch-Blöcke. In TypeScript können wir dank des strengen Typsystems die Fehlerverwaltung auf ein völlig neues Niveau heben. In diesem Artikel werden wir fortgeschrittene Techniken behandeln, die Ihnen helfen, zuverlässigeren und vorhersagbareren Code zu schreiben.
Das Problem des standardmäßigen try/catch in TypeScript
\n\nDer klassische try/catch-Block in TypeScript hat ein grundlegendes Problem: Der Typ des abgefangenen Fehlers ist unknown (oder any in älteren Versionen). Das bedeutet, dass TypeScript nicht weiß, mit welchem Objekt wir es zu tun haben.
try {\n // irgendein gefährlicher Code\n const result = JSON.parse('{"data": 123}');\n} catch (error) {\n // error hat den Typ unknown\n // Man kann nicht einfach error.message aufrufen\n console.log(error.message); // Kompilierungsfehler!\n}\n\nDies zwingt Entwickler entweder dazu, Typen zu ignorieren oder umständliche Prüfungen zu schreiben. Sehen wir uns an, wie wir dieses Problem auf fortgeschrittenem Niveau lösen können.
\n\n1. Erstellen eines Systems benutzerdefinierter Fehler mit Typisierung
\n\nAnstatt gewöhnliche Error zu werfen, erstellen Sie eine Hierarchie benutzerdefinierter Fehler. Dies ermöglicht TypeScript, Fehlertypen bereits zur Kompilierungszeit zu unterscheiden.
// Basisklasse für alle Anwendungsfehler\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// Konkrete Fehlertypen\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} nicht gefunden`, 'NOT_FOUND', 404);\n }\n}\n\nclass DatabaseError extends AppError {\n constructor(message: string) {\n super(message, 'DB_ERROR', 500);\n }\n}\n\n// Verwendung\nfunction findUser(id: string): User {\n if (!id) throw new ValidationError('ID ist erforderlich');\n // ... Suchlogik\n if (!user) throw new NotFoundError('Benutzer');\n return user;\n}\n\n2. Type Guards für die Fehlerbehandlung
\n\nUm verschiedene Fehlertypen sicher zu behandeln, verwenden Sie Type Guards. Dies ist ein zentrales Pattern für die Arbeit mit unknown in catch.
// Type Guard für AppError\nfunction isAppError(error: unknown): error is AppError {\n return error instanceof AppError;\n}\n\n// Type Guard für ValidationError\nfunction isValidationError(error: unknown): error is ValidationError {\n return error instanceof ValidationError;\n}\n\n// Fortgeschrittene Behandlung\ntry {\n const user = findUser(userId);\n} catch (error) {\n if (isValidationError(error)) {\n // TypeScript weiß, dass error ein ValidationError ist\n console.error(`Validierungsfehler: ${error.message}`);\n return { status: 400, message: error.message };\n }\n \n if (isAppError(error)) {\n // error ist ein AppError\n console.error(`Fehlercode: ${error.code}`);\n return { status: error.statusCode, message: error.message };\n }\n \n // Unbekannter Fehler\n if (error instanceof Error) {\n console.error('Unbekannter Fehler:', error.message);\n }\n \n return { status: 500, message: 'Interner Serverfehler' };\n}\n\n3. Result-Pattern (Either Monad)
\n\nDies ist eine Alternative zu Ausnahmen, die die Fehlerbehandlung auf Typebene explizit macht. Anstatt einen Fehler zu werfen, gibt die Funktion ein Objekt zurück, das entweder ein erfolgreiches Ergebnis oder einen Fehler enthalten kann.
\n\n// Typdefinitionen für das Result-Pattern\ntype Success<T> = { success: true; data: T };\ntype Failure<E> = { success: false; error: E };\ntype Result<T, E> = Success<T> | Failure<E>;\n\n// Hilfsfunktionen\nfunction success<T>(data: