Skip to content
TypeScript ts narrowing 3 min read

instanceof Narrowing

While typeof narrows primitives, the instanceof operator narrows values that come from classes or constructor functions. It checks whether an object’s prototype chain includes a given constructor’s prototype, and TypeScript uses that check to refine the static type inside the branch. This makes instanceof the natural tool for unions of class instances, built-in objects like Date and Error, and any value created with new. This page covers basic usage, error handling, inheritance, and the limits of instanceof.

The instanceof type guard

When a value can be one of several class instances, x instanceof SomeClass narrows it to that class within the truthy branch.

class Dog {
  bark() {
    return "Woof";
  }
}

class Cat {
  meow() {
    return "Meow";
  }
}

function speak(animal: Dog | Cat): string {
  if (animal instanceof Dog) {
    // animal: Dog
    return animal.bark();
  }
  // animal: Cat
  return animal.meow();
}

The right-hand side of instanceof must be a constructor function. TypeScript narrows the left side to the instance type of that constructor, so the class-specific methods become available.

Narrowing unknown and error handling

A catch clause in TypeScript types its variable as unknown (with useUnknownInCatchVariables on, the default under strict). instanceof is the idiomatic way to recover a useful type.

async function load(url: string): Promise<string> {
  try {
    const res = await fetch(url);
    return await res.text();
  } catch (err: unknown) {
    if (err instanceof Error) {
      // err: Error
      return `Failed: ${err.message}`;
    }
    return "Unknown failure";
  }
}

Because anything can be thrown in JavaScript — not just Error objects — the unknown type forces you to check before touching .message, and instanceof Error is exactly that check.

Built-in objects

instanceof works with built-in constructors, which is the cleanest way to distinguish, say, a Date from a string or a RegExp from a plain object.

function toISO(value: Date | string): string {
  if (value instanceof Date) {
    // value: Date
    return value.toISOString();
  }
  // value: string
  return new Date(value).toISOString();
}

Inheritance and the prototype chain

Because instanceof walks the prototype chain, a subclass instance satisfies instanceof checks for all of its ancestors. Order your checks from most specific to least specific.

class HttpError extends Error {
  constructor(public status: number, message: string) {
    super(message);
  }
}

function report(err: Error): string {
  if (err instanceof HttpError) {
    // err: HttpError — check the subclass first
    return `HTTP ${err.status}: ${err.message}`;
  }
  // err: Error
  return err.message;
}
OperatorNarrowsWorks on
typeofprimitivesstring, number, etc.
instanceofclass instancesanything with a prototype chain
inobject shapepresence of a property

instanceof is a runtime check, so it cannot narrow interfaces or type aliases — those have no constructor and are erased during compilation. Use a custom type guard for interface-only shapes.

When instanceof fails

instanceof compares against a specific constructor identity. Across different execution realms (iframes, worker threads, or separate module instances) the same logical class can have two distinct constructors, so the check returns false unexpectedly.

// In a browser, an array from another iframe:
// frameArray instanceof Array  ->  false (different realm)
// Prefer a structural check instead:
if (Array.isArray(value)) {
  // value: unknown[]
}

For cross-realm values, prefer realm-agnostic helpers like Array.isArray or custom guards over instanceof.

Best Practices

  • Use instanceof to narrow unions of class instances and built-ins like Date, Error, and RegExp.
  • In catch blocks, guard the unknown error with instanceof Error before reading its properties.
  • Check subclasses before their parents, since prototype-chain matching is inclusive.
  • Do not use instanceof for interfaces or type aliases — they have no runtime constructor.
  • Prefer Array.isArray over value instanceof Array for robustness across realms.
  • Combine instanceof with typeof and custom guards when a single operator cannot fully discriminate the union.
Last updated June 29, 2026
Was this helpful?