javascript / expert
Snippet
Typed Error Boundaries with Angular's ErrorHandler and RxJS catchError Chains
This pattern separates recoverable domain errors from truly unexpected ones by wrapping HTTP failures in a discriminated error subclass before they reach Angular's global ErrorHandler. Instead of a generic catch-all logging raw HttpErrorResponse objects, catchError translates transport-level failures into typed HttpDomainError instances carrying a narrow union of known codes. The global handler then uses instanceof to branch: domain errors get structured, actionable logging, while anything else falls through to a catch-all path, making silent misclassification of errors impossible at compile time.
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
Breakdown
1
class HttpDomainError extends Error {
Subclassing Error preserves stack traces while adding a discriminant field for exhaustive handling.
2
constructor(public readonly code: 'NOT_FOUND' | 'CONFLICT' | 'SERVER', message: string) {
The code parameter is a closed union, so downstream branching can be checked exhaustively by the compiler.
3
handleError(error: unknown): void {
Typing the parameter as unknown forces a runtime type check before any property access, avoiding unsafe assumptions.
4
if (error instanceof HttpDomainError) {
instanceof narrows the type so error.code is safely accessible only inside this branch.
5
return throwError(() => new HttpDomainError(code, `Order ${id} failed`));
Wrapping in a factory function (not a bare value) matches RxJS 7+'s lazy error creation contract.