What is type narrowing in TypeScript?
The short answer
Narrowing is how TypeScript reduces a broad type, usually a union, to a more specific one inside a branch of code. You write a runtime check the compiler understands, and within that branch TypeScript treats the value as the narrower type and lets you access its members safely. It is what makes union types practical to work with.
typeof for primitives
function format(value: string | number) { if (typeof value === 'string') { return value.toUpperCase(); // value is string here } return value.toFixed(2); // value is number here}instanceof for class instances
function logError(err: Error | string) { if (err instanceof Error) { console.log(err.message); // err is Error } else { console.log(err); // err is string }}The in operator for object shapes
type Dog = { bark: () => void };type Cat = { meow: () => void };function speak(pet: Dog | Cat) { if ('bark' in pet) { pet.bark(); // pet is Dog } else { pet.meow(); // pet is Cat }}Discriminated unions
The cleanest pattern is to give each member of a union a shared literal field, then switch on it:
type Shape = | { kind: 'circle'; radius: number } | { kind: 'square'; side: number };function area(shape: Shape) { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2; case 'square': return shape.side ** 2; }}The kind field is the discriminant. Once you check it, TypeScript knows exactly which member you have and which fields are available.
User defined type guards
When a check is not built in, write a function that returns a type predicate. The value is string return type tells the compiler what a true result means:
function isString(value: unknown): value is string { return typeof value === 'string';}function handle(input: unknown) { if (isString(input)) { input.toUpperCase(); // input is string }}Interview Tip
If you can model state as a discriminated union, do it. It is the pattern interviewers most like to see because it makes invalid combinations impossible to represent and gives you exhaustive switch checking. A { status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; message: string } union is far safer than three separate booleans.
Why interviewers ask this
Union types appear constantly in real code: API states, form values, event payloads. Narrowing is how you use them without reaching for any or non null assertions. Interviewers want to see that you let the compiler prove a value is safe before you touch it, and that you know the full toolkit: typeof, instanceof, in, discriminated unions, and custom type guards.