Narrowing: typeof instanceof discriminated unions
Use type guards to narrow union types at runtime with typeof, instanceof, and discriminated union patterns.
Narrowing: typeof instanceof discriminated unions is a free Frontend Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is Type Narrowing?
TypeScript starts with a wide type (e.g., string | number). Narrowing is the process of refining the type to a more specific one inside a conditional block. TypeScript tracks narrowing automatically.
typeof Narrowing
The typeof operator narrows primitives. Inside an if block, TypeScript knows the exact type.
function format(value: string | number | boolean): string {
if (typeof value === 'string') {
return value.toUpperCase(); // string here
}
if (typeof value === 'number') {
return value.toFixed(2); // number here
}
return String(value); // boolean here
}instanceof Narrowing
instanceof narrows class instances. TypeScript knows the specific class inside the block.
function processError(err: unknown) {
if (err instanceof Error) {
console.error(err.message); // Error methods available
} else if (err instanceof Response) {
console.error('HTTP error:', err.status);
} else {
console.error('Unknown:', err);
}
}Truthiness Narrowing
TypeScript narrows out null and undefined in truthy checks.
function printLength(value: string | null | undefined) {
if (value) {
console.log(value.length); // string here (null/undefined filtered)
}
}Equality Narrowing
Strict equality narrows to the literal type. Useful for discriminated unions and string enums.
function handle(action: 'submit' | 'cancel' | 'reset') {
if (action === 'submit') {
// action is exactly 'submit'
doSubmit();
}
}in Operator Narrowing
The in operator narrows object union types by checking which properties exist.
interface Cat { meow(): void; }
interface Dog { bark(): void; }
function speak(animal: Cat | Dog) {
if ('meow' in animal) {
animal.meow(); // Cat
} else {
animal.bark(); // Dog
}
}Discriminated Unions — Tag-Based Narrowing
A discriminated union has a shared literal type property (the discriminant). TypeScript narrows the union in switch/if based on that property.
type LoadingState = { status: 'loading' };
type SuccessState = { status: 'success'; data: User[] };
type ErrorState = { status: 'error'; message: string };
type State = LoadingState | SuccessState | ErrorState;
function render(state: State) {
switch (state.status) {
case 'loading': return '<Spinner />';
case 'success': return renderUsers(state.data); // state.data available
case 'error': return renderError(state.message);
}
}Type Predicates — Custom Type Guards
A type predicate function narrows the type for the caller. Use param is Type as the return type.
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'name' in value &&
typeof (value as User).name === 'string'
);
}
const data: unknown = await fetchJson('/api/me');
if (isUser(data)) {
console.log(data.name); // typed as User
}Assertion Functions
An assertion function throws if the condition fails, narrowing the type after the call.
function assertIsString(val: unknown): asserts val is string {
if (typeof val !== 'string') throw new Error('Expected string');
}
const val: unknown = getInput();
assertIsString(val);
console.log(val.toUpperCase()); // val is string hereExhaustiveness Checking
When a switch over a discriminated union leaves a never type in the default branch, TypeScript ensures all cases are handled. Add a default that assigns to never to get a compile error if a new variant is added.
function render(state: State): string {
switch (state.status) {
case 'loading': return '...';
case 'success': return state.data.length.toString();
case 'error': return state.message;
default:
const _exhaustive: never = state;
throw new Error('Unhandled state: ' + _exhaustive);
}
}Narrowing with Array.isArray
Array.isArray() narrows a value to the array type.
function processInput(input: string | string[]) {
if (Array.isArray(input)) {
return input.join(', '); // string[]
}
return input.toUpperCase(); // string
}Quick Check
Which narrowing technique uses a shared literal property to distinguish variants of a union type?
Recap: TypeScript Narrowing
typeof for primitives. instanceof for classes. in operator for object shapes. Truthiness filters null/undefined. Discriminated unions with a shared literal property enable exhaustive switch statements. Custom type predicates (is) for complex runtime checks. Exhaustiveness checking with never in defaults.
Frequently asked questions
Is the “Narrowing: typeof instanceof discriminated unions” lesson free?
Yes — the full text of “Narrowing: typeof instanceof discriminated unions” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.
What will I learn in “Narrowing: typeof instanceof discriminated unions”?
Use type guards to narrow union types at runtime with typeof, instanceof, and discriminated union patterns. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Frontend Academy?
No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Narrowing: typeof instanceof discriminated unions” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Frontend Academy lesson?
Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Generics: T extends and constraints
- Utility Types: Partial Required Pick Omit
- Mapped Types and Conditional Types
- Narrowing: typeof instanceof discriminated unions