Hintergrund: In JavaScript werden Standardfehler durch die Klasse Error beschrieben. In großen TypeScript-Projekten ist es wichtig, eine eigene Fehlerhierarchie für eine präzise Verarbeitung von Situationen zu verwenden, jedoch gibt es Nuancen in Bezug auf Typen, Vererbung und das Verhalten von 'instanceof'.
Problem: Bei der Vererbung von Error können Schwierigkeiten auftreten: das Prototyp wird verloren, 'instanceof' funktioniert nicht richtig, Typen können inkorrekt beschrieben sein und die Serialisierung führt oft zum Verlust des Stack-Trace. Ohne explizite Angabe der Eigenschaften können Bugs bei der Fehlerverarbeitung entstehen.
Lösung: Benutzerdefinierte Fehler sollten korrekt durch Erweiterung der Klasse Error definiert werden. Es ist wichtig, den Namen explizit anzugeben, den Prototyp manuell wiederherzustellen (wichtig für ES5 und bei der Kompilierung in CommonJS) und die Feldtypen der Fehler zu definieren.
Codebeispiel:
class ValidationError extends Error { code: number; constructor(message: string, code: number = 400) { super(message); Object.setPrototypeOf(this, ValidationError.prototype); this.name = 'ValidationError'; this.code = code; } } function process(user: string) { if (!user) throw new ValidationError('User required', 401); }
Schlüsselfunktionen:
Warum kann der Aufruf von Object.setPrototypeOf(this, ...) nicht weggelassen werden?
Wenn Object.setPrototypeOf(this, Class.prototype) nicht aufgerufen wird, funktioniert 'instanceof' ValidationError nicht bei der Kompilierung in ES5/CommonJS oder Babel. Dies führt dazu, dass die catch-Blöcke von ValidationError den Fehler nicht abfangen.
class CustomErr extends Error {} const err = new CustomErr('msg'); console.log(err instanceof CustomErr); // false ohne setPrototypeOf
Kann das Feld name bei benutzerdefinierten Fehlern weggelassen werden?
Wenn das this.name nicht gesetzt wird, sind der Fehlerstack und die Anzeige in Logs inkorrekt, was die Ursachenforschung und Kategorisierung von Fehlern erschwert.
Müssen Fehler serialisierbar sein und wenn ja, wie?
Fehler sollten korrekt serialisiert werden (z. B. für Logging oder Übertragung über das Netzwerk), sonst gibt JSON.stringify(new Error()) keine message und stack aus. Es sollte die Methode toJSON überschrieben werden.
class SerializableError extends Error { toJSON() { return { name: this.name, message: this.message, stack: this.stack }; } }
Im Projekt wurde einfach class MyError extends Error erstellt, wobei der Prototyp nicht wiederhergestellt wurde. Fehler wurden über if (err instanceof MyError) abgefangen, aber das funktionierte nicht, und der Code ignorierte stillschweigend die Behandlung kritischer Situationen.
Vorteile:
Nachteile:
Ein korrekter CustomError wurde implementiert, name, code und toJSON wurden explizit festgelegt, Tests deckten die Verarbeitung verschiedener Fehlerarten ab. In Logs und in den catch-Handlern wurde die Struktur der Fehler klarer, wodurch die Fehlersuche beschleunigt wurde.
Vorteile:
Nachteile: