"The most basic narrowing tool. Works on every primitive, lights up everywhere."
The value-level typeof
Recall from the Foundations track: typeof has two meanings. The value-level typeof runs at runtime and returns one of the seven strings: 'string', 'number', 'boolean', 'undefined', 'object', 'function', 'symbol', or 'bigint'. (Eight strings if you count null — but typeof null === 'object', a famous JavaScript quirk.)
TypeScript understands this exact set of strings as a narrowing operation. if (typeof x === 'string') inside that branch narrows x to string. Outside the branch, the type is whatever's left of the union minus string.
What typeof can narrow
The seven primitive types: string, number, boolean, undefined, object, function, symbol, bigint. That's the full set. Anything more specific (a particular object shape, a class instance, a literal) needs a different narrowing tool — typeof can only see as far as the primitive layer.
The typeof === 'object' trap
typeof null === 'object' returns true. This is a JavaScript bug from 1995 that became a feature. If you narrow on typeof x === 'object' and your union includes null, null is still in scope inside the branch.
The fix: add an explicit null check. if (typeof x === 'object' && x !== null). TypeScript even has this built into its narrowing — it knows that after the null check, x is the non-null object types only.
instanceof, in, or type predicates. Reach for the right tool for the type you're checking.