01 · Problem
Wofür braucht man das?
JavaScript darf beliebige Werte werfen; Grenzen zwischen Bibliotheken, Workern oder JSON verlieren dadurch Namen, Ursache und zuverlässige Typen.
02 · Denkmodell
Das mentale Modell
Am Systemrand wird unknown einmal in eine serialisierbare Fehlerform überführt. Intern transportiert ein discriminated union Erfolg oder Fehler explizit.
NormalizedError, NoThrowResult, isError, toNormalizedError, noThrow und aggregateErrors werden exportiert. Die Normalform verlangt stets einen Stack, noThrow erkennt nur native Promises und Umgebungslogik greift auf process.env zu; ein Error als legitimer Rückgabewert bleibt mehrdeutig.
03 · Kontrollfluss
Was passiert in welcher Reihenfolge?
- Synchronen Aufruf oder Promise in try/catch beziehungsweise await einschließen.
- Gefangenen unknown-Wert ohne weitere Throws in eine sichere Struktur umwandeln.
- Erfolg und Fehler über ein eindeutiges ok-Feld unterscheiden.
- An Prozessgrenzen nur JSON-sichere Felder übertragen und Ursachen begrenzen.
04 · Bauteile
Die entscheidenden Verträge
NormalizedError
Einheitliche Error-Unterklasse für unbekannte Fehlerwerte.
noThrow
Wandelt synchrones oder asynchrones Werfen in ein Result um.
toNormalizedError / isError
Normalisiert unknown und erkennt Error-artige Werte.
aggregateErrors
Fasst mehrere Fehler zu einer gemeinsamen Meldung zusammen.
05 · Build it yourself
Selbst implementieren
Ein explizites Result vermeidet die Mehrdeutigkeit zwischen Rückgabewert und Fehler.
- Result als Union mit ok: true oder ok: false definieren.
- unknown über instanceof und sichere String-Konvertierung normalisieren.
- Async-Aufrufe immer mit await im selben try-Block ausführen.
type Result<T> =
| { ok: true; value: T }
| { ok: false; error: { name: string; message: string; stack?: string } };
async function capture<T>(run: () => T | PromiseLike<T>): Promise<Result<T>> {
try { return { ok: true, value: await run() }; }
catch (value) {
const error = value instanceof Error ? value : new Error(String(value));
return { ok: false, error: { name: error.name, message: error.message, stack: error.stack } };
}
}06 · Verifizieren
Was du testen solltest
- Error, String, null und ein Objekt mit werfender toString-Methode werden ohne Folgefehler normalisiert.
- Thenables und native Promises liefern dieselbe Result-Form.
- Ein regulär zurückgegebener Error bleibt durch ok: true eindeutig ein Wert.
07 · Grenzen
Kompromisse und Stolperfallen
- Result macht Fehler im Typ sichtbar, erfordert aber explizites Branching an jedem Aufruf.
- Stacks helfen beim Debugging, können jedoch Pfade oder andere interne Daten verraten.
- Serialisierbare Fehler verlieren Prototyp und nicht standardisierte Eigenschaften.
Fehlernormalisierung darf selbst niemals werfen. Schütze deshalb auch String-Konvertierung und cause-Rekursion.
08 · Weiterdenken