Narrowing with typeof and in
📚 What is Type Narrowing in TypeScript? Type narrowing is when TypeScript gets more specific about a type inside a conditional block. If a variable is string | number and you write if (typeof x === 'string') { ... }, TypeScript KNOWS inside that block x is a string -- so it unlocks string methods.…

Appy Says…
A value typed as string | number can be either. Before you call string methods on it, you need to prove it's actually a string — that's narrowing. TypeScript tracks your checks and narrows the type inside if blocks automatically.
What is Type Narrowing?
Type narrowing is TypeScript's ability to deduce a more specific type within a code block based on a conditional check. After a guard, the type is narrowed inside the branch.
- •
typeofguard:if (typeof x === 'string') { x.toUpperCase(); } - •
instanceofguard:if (x instanceof Date) { x.toISOString(); } - •Truthiness:
if (user) { user.name; }— narrows out null/undefined - •Equality:
if (x === null) { ... } else { // x is not null } - •in operator:
if ('name' in obj) { obj.name; } - •Discriminated union:
if (shape.kind === 'circle') { shape.radius; }
Think of it like verifying a Minecraft item before using it
Before crafting, you check what's in the slot: 'if this is a sword, sharpen it; if it's a pickaxe, use it to mine'. TypeScript does the same: your runtime check teaches TypeScript what type it is so it can let you call the right methods.
How It Works
- •1. Function receives
value: string | number | null - •2.
if (typeof value === 'string') { ... }— inside: value is string - •3.
else if (typeof value === 'number') { ... }— inside: value is number - •4.
else { ... }— inside: value is null (all other cases exhausted) - •5. TypeScript's control flow analysis tracks narrowing through if/else/switch/early return
- •6. Type guard function:
function isUser(x: any): x is User { return 'name' in x; }
Real-World Examples
- •
if (typeof id === 'string') { id.trim() } else { id.toFixed(0) } - •
if (err instanceof Error) { console.error(err.message) } - •
if (response.ok) { const data = await response.json() } // TypeScript narrows here - •Discriminated union:
switch (action.type) { case 'ADD': action.payload; }
Key Facts
- •TypeScript's narrowing is flow-based — it follows every branch and knows the type at each point
- •Early return is the cleanest narrowing pattern:
if (!user) return; user.name; // safe - •User-defined type guards:
x is Userreturn type lets you teach TypeScript about custom runtime checks - •TypeScript 4.4 improved narrowing of aliased conditions —
const isString = typeof x === 'string'; if (isString) { ... }
Watch Out!
Don't use typeof null === 'object' to check for null — it returns 'object' (a JavaScript legacy bug). Always check === null explicitly. For an object that might be null: if (x !== null && typeof x === 'object').
Remember
typeof, instanceof, truthiness, and discriminated union checks all narrow types in TypeScript. After the check, TypeScript knows the specific type inside the branch — you get full intellisense for that narrowed type.
What You Learned
- •Narrowing: TypeScript tracks type guards and deduces specific types inside if/switch branches
- •typeof, instanceof, truthiness, discriminated unions — all narrow the type automatically
- •Unlocks: safe handling of union types, exhaustive switch statements, working with unknown API data
Key Facts
- →TypeScript's narrowing is flow-based — it follows every branch and knows the type at each point
- →Early return is the cleanest narrowing pattern:
if (!user) return; user.name; // safe - →User-defined type guards:
x is Userreturn type lets you teach TypeScript about custom runtime checks - →TypeScript 4.4 improved narrowing of aliased conditions —
const isString = typeof x === 'string'; if (isString) { ... }
Real-World Examples
Remember
typeof, instanceof, truthiness, and discriminated union checks all narrow types in TypeScript. After the check, TypeScript knows the specific type inside the branch — you get full intellisense for that narrowed type.
Quick Quiz
typeof x === 'string' narrows?