TypeScript · Types that pay for themselves

How does type narrowing work?

Mediumnarrowingcontrol-flowunionsUpdated Oct 2026
Quiz this track

Short answer

Narrowing is how TypeScript refines a union based on control flow, such as typeof, in, equality checks or instanceof. The refined type applies only where the check guarantees it.

The compiler performs control-flow analysis. It tracks the possible types of a value through assignments, conditionals, loops and early returns. When a runtime check rules out a member of a union, the type inside that branch is reduced to the remaining members.

ts
type Result =
  | { ok: true; value: string }
  | { ok: false; error: Error };

function handle(result: Result): string {
  if (result.ok) {
    return result.value;
  }

  return result.error.message;
}

Checks the compiler understands

  • typeof for primitive types
  • in for property presence on object variants
  • instanceof for class instances
  • Literal equality and switch statements for discriminated unions
  • User-defined type guards and assertion functions

Narrowing is not always preserved inside callbacks. A value that was narrowed outside a function may be widened again inside a closure because the closure could run later, after the value changes. Capture the narrowed value in a local constant if the callback needs the refined type.

Narrowing follows the code, not your intent. If the check is incomplete, the compiler keeps the wider type, which is usually a sign that a runtime case is unhandled.

Follow-ups you should expect

Interviewers use these to check whether the first answer was understanding or memorisation.

  1. 1Why can narrowing be lost inside a callback?
  2. 2How does an early return affect narrowing?
  3. 3What is an assertion function?
  4. 4How does switch narrowing work with discriminated unions?

Say it in the interview

Narrowing is the compiler refining a union based on control flow. typeof handles primitives, in checks properties, instanceof handles classes, and a discriminated union's literal field handles object variants. The narrowed type only applies where the check holds. I structure code so the checks are explicit and the compiler can follow them, rather than asserting the type.

Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.

Related questions

Reading is step one

Now say it out loud.

Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live TypeScript mock interview and get scored feedback on depth, clarity and confidence.