"JavaScript lets you throw anything. TypeScript types catches as unknown so you handle that fact."
The unknown catch parameter
JavaScript permits throw 'string', throw 42, throw { custom: 'object' }, throw new Error(...), or any other value. Your catch block must handle whatever shape arrived. With strict mode's useUnknownInCatchVariables flag (on by default in modern projects), the catch parameter's type is unknown, forcing you to narrow before accessing properties.
The pattern: catch (e) { if (e instanceof Error) console.log(e.message); else console.error('unknown error', e); }. The narrowing makes the access safe.
Why this matters more for async
Promise rejections aren't typed either. fetch().then(...).catch(e => ...) — e is whatever was rejected with, which could be anything. async/await brings these into try/catch, where the unknown rule kicks in. Without strict mode, you might write e.message and silently crash on a string-throwing API.
Custom error classes for richer narrowing
When you throw, prefer extending Error: class NetworkError extends Error { constructor(public status: number) { super() } }. Then narrow with instanceof NetworkError to access the typed fields. This is the same discriminated-union pattern from earlier, applied to error hierarchies.
e as untrusted input. JavaScript's flexibility means catch handlers must validate before accessing. Narrow with instanceof, branch on shape, fall back gracefully on unknown values.