Glossary / Type narrowing

Type narrowing

Type narrowing is TypeScript's control-flow analysis that refines a broad type to a more specific type after guards and assignments. It allows safe use of union members while preserving runtime JavaScript checks as the source of evidence.

Narrowing mechanisms

Common guards include typeof, equality checks, in, instanceof, discriminant comparisons, truthiness, and user-defined type predicates. Array.isArray narrows arrays; assertion functions can tell the checker that a condition must hold or throw. Control-flow analysis tracks branches, returns, and assignments, but may widen when values are mutated or captured.

function format(value: string | number) {
  if (typeof value === 'string') return value.toUpperCase();
  return value.toFixed(2);
}

Optional chaining is not a general type guard for unrelated values; non-null assertions (!) bypass checking rather than proving safety. A predicate such as value is User is trusted by the compiler, so an incorrect implementation creates unsoundness. Prefer checking each required field and handling arrays, null, and malformed input.

Runtime boundaries

Values from JSON, network, storage, and JavaScript callers are not made trustworthy by TypeScript annotations. Use unknown at boundaries and narrow with actual runtime conditions or a schema validator. Keep validation and type guards aligned as the schema evolves.

Interview questions

  1. What is narrowing? Refining a union or broad type based on runtime control-flow evidence.
  2. Does value! validate a value? No; it suppresses a nullability diagnostic.
  3. Can custom predicates be wrong? Yes; TypeScript trusts the declared predicate.
  4. Why start external values as unknown? It requires checking before use, unlike any.
  5. What are common narrowing guards? typeof, instanceof, in, equality/discriminants, and validated predicates.

References