Control-Flow Narrowing

TypeScript tracks checks such as typeof, equality comparisons, in, instanceof, and early returns to refine a variable's type along a control-flow path. This is called narrowing. It is usually better to narrow using a real runtime fact than to force a type assertion.

For object unions, checking a property or a discriminant can identify the current variant. Be careful with optional properties and overlapping object shapes: a check is only as strong as the runtime fact it establishes.

Narrow an unknown input
function describe(value: string | number): string {
  if (typeof value === "number") {
    return value.toFixed(1);
  }
  return value.toUpperCase();
}
💡 Inside the first branch value is a number; after the return, the remaining path is a string. TypeScript follows the control flow to determine the safe type.
Key Takeaways
  • Runtime checks can narrow unions.
  • Early returns often simplify the remaining type.
  • Assertions do not perform runtime validation.
Knowledge Check

After `if (typeof value === "number")`, what is the type of `value` inside that branch for `string | number`?