TypeScript verwendet try, catch, finally und throw von JavaScript. Der TypeScript-spezifische Teil ist die catch-Variable: Mit aktivem strict hat sie den Typ unknown, also prüfst du, was geworfen wurde, bevor du sie verwendest.
Die Seite zur Laufzeit (wie sich der Stack abbaut, welche eingebauten Fehlertypen es gibt) wird unter try/catch in JavaScript behandelt.
Warum die catch-Variable unknown ist
JavaScript kann alles werfen: einen Error, einen String, eine Zahl, undefined, ein Objekt aus einer Bibliothek. TypeScript kann nicht wissen, was, daher ist die Variable unter strict (dem Flag useUnknownInCatchVariables) unknown, und du musst sie vor der Verwendung einengen:
index.ts(5,34): error TS18046: 'e' is of type 'unknown'.
Ohne strict ist e any, und derselbe Code kompiliert und geht dann zur Laufzeit schief, wenn ein String geworfen wird: e.message ist undefined, und alles Tiefere, etwa e.message.length, wirft einen TypeError. Mit einer Annotation kommst du auch nicht heraus: catch (e: Error) ist der Fehler TS1196 (Catch clause variable type annotation must be 'any' or 'unknown' if specified).
Den Fehler einengen
instanceof Error deckt alles ab, was auf Error aufbaut, einschließlich TypeError, SyntaxError, RangeError und deiner eigenen Unterklassen. Für alles andere wandelst du den Wert ersatzweise in einen String um. Eine kleine Hilfsfunktion hält die Aufrufstellen kurz:
Ein catch, der nur manche Fehler behandelt, sollte den Rest erneut werfen: if (!(e instanceof ValidationError)) throw e;. Unbekannte Fehler zu verschlucken verdeckt echte Bugs.
Fehler werfen
throw akzeptiert jeden Ausdruck, und TypeScript schränkt das nicht ein. Wirf trotzdem Error-Objekte: Sie tragen einen Stacktrace, und jede Prüfung mit instanceof Error in der Codebasis hängt davon ab.
Eine Funktion, die immer wirft, hat den Rückgabetyp never, den TypeScript für das Narrowing nach dem Aufruf nutzt:
Seit ES2022 nimmt Error ein zweites Argument mit cause, das einen Fehler auf niedrigerer Ebene mit dem verbindet, den du wirfst: throw new Error("could not load settings", { cause: e }). Der Aufrufer kann err.cause lesen (typisiert als unknown).
Eigene Fehlerklassen
Eine Unterklasse von Error lässt Aufrufer Fehlschläge mit instanceof unterscheiden und trägt zusätzliche Daten. Setze name, denn der geerbte ist "Error" und erscheint in Logs und in String(err):
Prüfe die spezifischste Klasse zuerst, da ein NotFoundError auch ein HttpError ist. Der Modifikator override ist hier optional, außer noImplicitOverride ist aktiv.
Ältere Anleitungen fügen jedem Fehlerkonstruktor Object.setPrototypeOf(this, new.target.prototype) hinzu. Das war nötig, als Klassen zu ES5-Funktionen herunterkompiliert wurden, wodurch instanceof bei Unterklassen von Error nicht mehr funktionierte. TypeScript 7 unterstützt target: "es5" nicht mehr (es meldet TS5108: Option 'target=ES5' has been removed), und mit ES2015 oder neuer funktioniert das native class ... extends Error, wie oben.
Das Result-Muster
TypeScript verfolgt nicht, welche Fehler eine Funktion werfen kann, also erinnert nichts Aufrufer daran, sie zu behandeln. Bei erwarteten Fehlschlägen (ungültige Eingabe, ein fehlender Datensatz) bringt ein Rückgabewert, der Erfolg oder Fehlschlag meldet, den Fehler ins Typsystem:
r.value vor der Prüfung von r.ok zu lesen ist ein Compilerfehler, und genau darum geht es. Nimm Exceptions für das Unerwartete (Bugs, eine abgebrochene Verbindung) und Result-Werte für Ergebnisse, über die der Aufrufer entscheiden muss.
Fehler in asynchronem Code
Ein abgelehntes Promise wird beim await zu einem geworfenen Fehler, also funktioniert try/catch um await genauso, und die Variable ist wieder unknown. Der einzige Unterschied ist .catch() auf einem Promise: Dessen Callback-Parameter ist als any typisiert, nicht als unknown, annotiere ihn also selbst (.catch((e: unknown) => ...)). Beispiele stehen unter async/await.
Häufige Fehler
e.messageohne Einengen lesen. Unterstrictkompiliert es nicht; ohnestrictbricht es, wenn etwas anderes als einErrorgeworfen wird.- Alles fangen und weitermachen. Behandle die Fehler, die du erwartest, und wirf den Rest erneut.
namebei eigenen Fehlern vergessen. Dann steht in den Logs bei allenError.- Strings werfen.
throw "failed"hat keinen Stacktrace und besteht keine Prüfung mitinstanceof Error.
Häufig gestellte Fragen
Welchen Typ hat der Fehler in einem catch-Block in TypeScript?
unknown, wenn strict aktiv ist (das Flag useUnknownInCatchVariables), sonst any. JavaScript kann jeden Wert werfen, nicht nur Error-Objekte, deshalb zwingt dich TypeScript zu prüfen. Enge ihn mit if (e instanceof Error) ein, bevor du e.message liest.
Kann ich die catch-Variable in TypeScript als Error typisieren?
Nein. catch (e: Error) ist der Fehler TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. Nur unknown und any sind erlaubt, weil nichts garantiert, was geworfen wurde. Enge stattdessen im Block ein.
Wie werfe ich in TypeScript einen Fehler?
Genau wie in JavaScript: throw new Error("message") oder eine Instanz einer eingebauten oder eigenen Unterklasse wie new RangeError(...). TypeScript lässt dich jeden Wert werfen, aber Error-Objekte zu werfen erhält einen Stacktrace und lässt Prüfungen mit instanceof Error funktionieren.
Wie erstelle ich in TypeScript eine eigene Fehlerklasse?
Erweitere Error, rufe super(message, options) auf und setze name: class NotFoundError extends Error { name = "NotFoundError"; }. Zusätzliche Felder kommen in den Konstruktor. Mit jedem unterstützten Target (ES2015 und neuer) funktioniert instanceof NotFoundError ohne Prototyp-Korrektur.
Hat TypeScript Checked Exceptions oder eine throws-Klausel?
Nein. Der Typ einer Funktion sagt nichts darüber, was sie werfen kann, und der Compiler prüft nie, ob Fehler behandelt werden. Für Fehler, die ein Aufrufer behandeln soll, gibst du sie als Werte mit einem Result-Typ zurück (eine Discriminated Union aus Erfolg und Fehlschlag), damit das Typsystem sie prüft.