0Pricing
Frontend Academy · Lesson

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 here

Exhaustiveness 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

  1. Generics: T extends and constraints
  2. Utility Types: Partial Required Pick Omit
  3. Mapped Types and Conditional Types
  4. Narrowing: typeof instanceof discriminated unions
← Back to Frontend Academy