TypeScript uses JavaScript's try, catch, finally and throw. The TypeScript-specific part is the catch variable: with strict on, it has type unknown, so you check what was thrown before using it.
The runtime side (how the stack unwinds, which built-in error types exist) is covered in JavaScript try/catch.
Why the catch Variable Is unknown
JavaScript can throw anything: an Error, a string, a number, undefined, an object from a library. TypeScript cannot know which, so under strict (the useUnknownInCatchVariables flag) the variable is unknown and you must narrow it before use:
index.ts(5,34): error TS18046: 'e' is of type 'unknown'.
Without strict, e is any and the same code compiles, then goes wrong at runtime when a string is thrown: e.message is undefined, and anything deeper, such as e.message.length, throws a TypeError. You cannot annotate your way out either: catch (e: Error) is error TS1196 (Catch clause variable type annotation must be 'any' or 'unknown' if specified).
Narrowing the Error
instanceof Error covers everything built on Error, including TypeError, SyntaxError, RangeError and your own subclasses. For anything else, fall back to converting the value to a string. A small helper keeps call sites short:
A catch that only handles some errors should rethrow the rest: if (!(e instanceof ValidationError)) throw e;. Swallowing unknown errors hides real bugs.
Throwing Errors
throw accepts any expression, and TypeScript does not restrict it. Throw Error objects anyway: they carry a stack trace, and every instanceof Error check in the codebase depends on it.
A function that always throws has the return type never, which TypeScript uses for narrowing after the call:
Since ES2022, Error takes a second argument with a cause, which chains a low-level error to the one you throw: throw new Error("could not load settings", { cause: e }). The caller can read err.cause (typed unknown).
Custom Error Classes
A subclass of Error lets callers tell failures apart with instanceof and carry extra data. Set name, because the inherited one is "Error" and it appears in logs and in String(err):
Check the most specific class first, since a NotFoundError is also an HttpError. The override modifier is optional here unless noImplicitOverride is on.
Older guides add Object.setPrototypeOf(this, new.target.prototype) to every error constructor. That was needed when compiling classes down to ES5 functions, where instanceof broke for Error subclasses. TypeScript 7 no longer supports target: "es5" (it reports TS5108: Option 'target=ES5' has been removed), and with ES2015 or later the native class ... extends Error works, as above.
The Result Pattern
TypeScript does not track which errors a function can throw, so nothing reminds callers to handle them. For failures that are expected (invalid input, a missing record), returning a value that says "success or failure" puts the error into the type system:
Reading r.value before checking r.ok is a compile error, which is the point. Use exceptions for the unexpected (bugs, a broken connection) and Result values for outcomes the caller must decide about.
Errors in Async Code
A rejected promise becomes a thrown error at the await, so try/catch around await works the same way, and the variable is again unknown. The one difference is .catch() on a promise: its callback parameter is typed any, not unknown, so annotate it yourself (.catch((e: unknown) => ...)). See async/await for examples.
Common Mistakes
- Reading
e.messagewithout narrowing. Understrictit does not compile; withoutstrictit breaks on non-Errorthrows. - Catching everything and continuing. Handle the errors you expect and rethrow the rest.
- Forgetting
nameon custom errors. Logs then sayErrorfor all of them. - Throwing strings.
throw "failed"has no stack trace and failsinstanceof Errorchecks.
Frequently Asked Questions
What is the type of the error in a TypeScript catch block?
unknown when strict is on (the useUnknownInCatchVariables flag), otherwise any. JavaScript can throw any value, not just Error objects, so TypeScript makes you check. Narrow it with if (e instanceof Error) before reading e.message.
Can I type the catch variable as Error in TypeScript?
No. catch (e: Error) is error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. Only unknown and any are allowed, because nothing guarantees what was thrown. Narrow inside the block instead.
How do I throw an error in TypeScript?
The same as in JavaScript: throw new Error("message"), or an instance of a built-in or custom subclass like new RangeError(...). TypeScript lets you throw any value, but throwing Error objects keeps a stack trace and makes instanceof Error checks work.
How do I create a custom error class in TypeScript?
Extend Error, call super(message, options), and set name: class NotFoundError extends Error { name = "NotFoundError"; }. Extra fields go in the constructor. With any supported target (ES2015 and later), instanceof NotFoundError works with no prototype fix.
Does TypeScript have checked exceptions or a throws clause?
No. A function's type says nothing about what it may throw, and the compiler never checks that errors are handled. For errors a caller is expected to handle, return them as values with a Result type (a discriminated union of success and failure) so the type system does check.