0 Tage Serie
javascript / expert
Snippet

Typisierte Fehlergrenzen mit Angulars ErrorHandler und RxJS catchError-Ketten

Dieses Muster trennt behebbare Domänenfehler von wirklich unerwarteten, indem HTTP-Fehler vor dem Erreichen von Angulars globalem ErrorHandler in eine diskriminierte Fehler-Unterklasse gepackt werden. Statt eines generischen Catch-all, das rohe HttpErrorResponse-Objekte protokolliert, übersetzt catchError Transportfehler in typisierte HttpDomainError-Instanzen mit einer engen Union bekannter Codes. Der globale Handler verzweigt dann per instanceof: Domänenfehler erhalten strukturiertes, verwertbares Logging, alles andere fällt in einen Catch-all-Pfad — eine stille Fehlklassifizierung wird so zur Compile-Zeit unmöglich.

snippet.js
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { ErrorHandler, Injectable, inject } from '@angular/core';
import { catchError, throwError, Observable } from 'rxjs';
class HttpDomainError extends Error {
constructor(public readonly code: 'NOT_FOUND' | 'CONFLICT' | 'SERVER', message: string) {
super(message);
this.name = 'HttpDomainError';
}
}
@Injectable()
class GlobalErrorHandler implements ErrorHandler {
handleError(error: unknown): void {
if (error instanceof HttpDomainError) {
console.error(`[domain:${error.code}]`, error.message);
return;
}
console.error('[unhandled]', error);
}
}
function loadOrder$(id: string, http: { get: (u: string) => Observable<unknown> }) {
return http.get(`/orders/${id}`).pipe(
catchError((err: { status?: number }) => {
const code = err.status === 404 ? 'NOT_FOUND' : err.status === 409 ? 'CONFLICT' : 'SERVER';
return throwError(() => new HttpDomainError(code, `Order ${id} failed`));
})
);
}
angular
Erklärung
1
class HttpDomainError extends Error {
Die Unterklasse von Error bewahrt Stacktraces und fügt ein Diskriminanzfeld für erschöpfende Behandlung hinzu.
2
constructor(public readonly code: 'NOT_FOUND' | 'CONFLICT' | 'SERVER', message: string) {
Der Parameter code ist eine geschlossene Union, sodass nachgelagerte Verzweigungen vom Compiler erschöpfend geprüft werden können.
3
handleError(error: unknown): void {
Die Typisierung als unknown erzwingt eine Laufzeitprüfung vor jedem Property-Zugriff und verhindert unsichere Annahmen.
4
if (error instanceof HttpDomainError) {
instanceof grenzt den Typ ein, sodass error.code nur innerhalb dieses Zweigs sicher zugänglich ist.
5
return throwError(() => new HttpDomainError(code, `Order ${id} failed`));
Das Einwickeln in eine Factory-Funktion (kein blanker Wert) entspricht dem lazy Error-Erzeugungsvertrag von RxJS 7+.