QuestionsTypeScript

Type guards & narrowing

NarrowingMediumTypeScript

How does TS narrow types? Write a user-defined type guard, and show exhaustiveness with never.

What it tests

typeof/in/instanceof narrowing plus custom `x is T` predicates — and exhaustiveness with never.

Approach & answer

TS narrows a union based on control flow: typeof for primitives, instanceof for classes, `in` for property presence, and equality against literals. A user-defined guard `function isCat(a): a is Cat` teaches TS a custom narrowing rule. For exhaustiveness, assign the value to a never in the default branch — add a new union member and forget to handle it, and the code stops compiling. Key subtlety: the `a is Cat` return type is a TYPE PREDICATE — you're asserting to the compiler that a truthy return means the argument is a Cat, and TS trusts you, so the runtime check inside must actually be correct (a wrong predicate silently corrupts every downstream type). Truthiness narrowing (if (value) ...) removes null/undefined and other falsy types; the non-null assertion value! does it without a check (use sparingly). Prefer discriminated-union narrowing (a tag field) over `in`/typeof gymnastics when you control the types — it's cheaper to read and the exhaustiveness check comes for free.

Use this technique when

Discriminating shapes without a tag, validating unknown API data, and ensuring every case of a union is handled.

References

ts