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. This is 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, and your progress syncs across the web and the CoddyKit app. The Frontend Academy course includes 4 lessons in total.
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.
Learn HTML with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 41
- Lessons
- 163
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. 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. The Frontend Academy course includes 4 lessons in total.
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, so you can start here or from the beginning and move at your own pace. This is lesson 4 of 4.
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