try / catch Error Handling & catch Narrowing
Last updated: 2026-10-22
try / catch Error Handling & catch Narrowing
TypeScript error handling has two pain points: the catch variable is unknown under strict mode (JS can throw anything, so the compiler dare not assume Error), requiring narrowing before you can touch .message; and "this may throw" is invisible in type signatures. The fixes: narrow unknown with instanceof, and for expected failures use the Result pattern, encoding success/failure right in the return type.
| Group | Engineering & Practice |
|---|---|
| Kind | Syntax |
| Strict mode | Not required |
ℹ️ Info The editor transpiles TypeScript to JavaScript and runs it (no type checking); see each example's Output for type-check results.
📝 Syntax
// 1) Narrowing unknown
try {
risky();
} catch (err: unknown) {
if (err instanceof Error) console.log(err.message);
else console.log("unknown throw:", err);
}
// 2) Result pattern: a union type encoding success or failure
type Result<T> = { ok: true; value: T } | { ok: false; error: string };
function attempt<T>(fn: () => T): Result<T> {
try { return { ok: true, value: fn() }; }
catch { return { ok: false, error: "failed" }; }
}
⚙️ Parameters
| unknown | The catch variable's type (the default under strict mode): it can hold anything, but must be narrowed before use — that is where the safety comes from |
|---|---|
| instanceof | The most common narrowing tool: after err instanceof Error you get .message/.stack; works with custom error classes too |
| Result<T> | A hand-rolled return type: { ok: true; value: T } | { ok: false; error }. Callers must check ok first; each branch exposes only value or error |
Returns:The Result pattern's return type is a discriminated union: value exists only when ok is true, error only when false — possible failure is encoded in the type, and callers cannot compile without handling the failure branch.
▶ Example
Edit the code below and click Try it to see the transpiled result:
Output:
caught RangeError: not a positive integer: abc
7 0
{"ok":true,"value":42}
failed: invalid input
(Runtime output after transpilation; .message is only reachable after instanceof narrowing — touching err.message without it yields error TS18046)