Type narrowing
Type narrowing is TypeScript's ability to refine a value's type within a conditional branch based on runtime checks — typeof, instanceof, in operator, equality checks, custom type predicates. Narrowing lets a parameter typed as 'string | number' be treated as a string inside a 'typeof x === string' branch without explicit casts.
Narrowing is the everyday workhorse of TypeScript ergonomics: it's what makes union types pleasant to work with. The narrowing operators are: typeof for primitives, instanceof for class instances, in for property existence checks, equality for literal types, and user-defined type predicates ('x is Foo') for arbitrary refinements. Modern TypeScript narrows aggressively: control-flow analysis tracks assignments, early returns narrow the rest of the function, and discriminated unions narrow via the discriminant check. When narrowing doesn't kick in, the cause is usually mutable state (the type system can't prove the narrowed type still holds after a function call) or a missing discriminant.
Related terms
- TypeScript strict mode
TypeScript's strict mode enables a bundle of compiler flags that produce stricter type checking — noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, alwaysStrict, strictPropertyInitialization, noImplicitThis.
- Discriminated union
A discriminated union is a union type whose variants share a literal-typed property (the discriminant) that uniquely identifies each variant — typically named 'type', 'kind', or 'tag'.
- Branded type
A branded type is a TypeScript pattern that distinguishes structurally-identical types by adding a phantom 'brand' property — UserId and ProductId can both be string at runtime but cannot be assigned to each other at compile time.