Type narrowing is how TypeScript refines a broad type to a more specific one within a code branch, using checks like typeof, instanceof, in, equality, and custom type-guard functions.
Inside if (typeof x === 'string') { ... }, TypeScript KNOWS x is a string in that block, enabling string methods safely. Guards include: typeof (primitives), instanceof (classes), the in operator ('id' in obj), equality/discriminant checks (if (r.ok)), and truthiness. Custom type guards are functions returning a type predicate: function isUser(x: unknown): x is User { return typeof (x as any).id === 'number' } — after calling it, the compiler narrows the type. Narrowing is the safe alternative to assertions: it proves the type with a runtime check the compiler understands.
Handling a union safely: function render(r: SuccessResp | ErrorResp) { if (r.status === 'error') { showError(r.message) } else { showData(r.data) } } — the discriminant narrows each branch to the right fields.
You receive unknown JSON and want to safely treat it as a User only if it really is one. What's the idiomatic TypeScript approach?