TypeScript Tutorial: Error Handling
try/catch with unknown errors, custom error classes, Result types, and building robust error handling patterns.
The Problem: Errors Are Untyped
TypeScript's `catch` blocks give you `unknown` — not `Error`. You must narrow the type before using it.
```typescript
try {
doSomethingRisky();
} catch (error) {
// error is `unknown` — not Error!
if (error instanceof Error) {
console.error(error.message); // safe
}
}
```
---
Custom Error Classes
```typescript
class AppError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly statusCode: number = 500
) {
super(message);
this.name = "AppError";
}
}
class NotFoundError extends AppError {
constructor(resource: string, id: number) {
super(`${resource} ${id} not found`, "NOT_FOUND", 404);
}
}
```
---
The Result Pattern
Inspired by Rust — instead of throwing, return either a success or failure value:
```typescript
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
```
---
What's Next?
You've completed the core TypeScript track. Explore **Advanced Types** for mapped types, conditional types, and template literal types.
What you'll learn in this TypeScript error handling tutorial
This interactive TypeScript tutorial has 12 hands-on exercises. Estimated time: 20 minutes.
- Why errors are unknown in TypeScript — Unlike Java or C#, JavaScript can throw anything — strings, numbers, objects, not just Error instances. TypeScript refle…
- Custom error classes — specific error types — Custom error classes let you distinguish between different failure modes. `instanceof` checks are cleaner than checking …
- Error factories — consistent error creation — Error factory functions standardize how errors are created across your codebase. Instead of `throw new Error(...)` scatt…
- The Result type — no throwing — Instead of throwing errors (which break control flow), some codebases use a `Result` type that wraps either a success va…
- Chaining Results — transforming safely — When you have multiple operations that can each fail, you often need to chain them. A `map` function on Result lets you …
- try/catch with typed errors — a helper function — Wrapping `try/catch` in a helper function lets you standardize error handling and remove repetitive error-narrowing code…
- finally — always runs — `finally` runs after `try` and `catch`, regardless of whether an error was thrown. It's used for cleanup: closing connec…
- Re-throwing errors — filter what you handle — Sometimes you can only handle specific error types and need to pass others up the call stack. Re-throwing lets you handl…
- Assertion functions — throw or guarantee — An assertion function throws if a condition isn't met and otherwise tells TypeScript "after this call, the condition is …
- Error aggregation — collect all failures — Sometimes you want to collect ALL errors from a validation, not just the first one. Return all errors in one pass so use…
- Error serialization — errors across boundaries — Errors can't be directly serialized to JSON — you must extract the relevant fields. This is essential when sending error…
- Typed error boundaries — catch what you expect — A well-designed system only throws errors of known types. You can build error handlers that are typed to specific error …
TypeScript Error Handling concepts covered
- The Problem: Errors Are Untyped
- Custom Error Classes
- The Result Pattern
- What's Next?