CodeWithMihir

TypeScript Tutorial

TypeScript instanceof Narrowing Explained

Learn how instanceof narrowing works in TypeScript for classes, Date objects, Error objects, and practical runtime checks.

Welcome back! I am Mihir, and in this lesson we will learn instanceof narrowing in TypeScript.

instanceof checks whether a value was created from a class or constructor function.


Basic instanceof Narrowing

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

  return value.toUpperCase();
}

Inside the if, TypeScript knows value is a Date.

Outside it, TypeScript knows value is a string.


Narrowing Custom Classes

class Car {
  drive() {
    console.log("Driving");
  }
}

class Bike {
  ride() {
    console.log("Riding");
  }
}

function move(vehicle: Car | Bike) {
  if (vehicle instanceof Car) {
    vehicle.drive();
  } else {
    vehicle.ride();
  }
}

instanceof Car narrows vehicle to the Car class.


instanceof with Error

This is useful in error handling.

function handleError(error: unknown) {
  if (error instanceof Error) {
    console.log(error.message);
  } else {
    console.log("Unknown error");
  }
}

Because catch values can be unknown, instanceof Error is a practical check.


instanceof Needs Runtime Values

instanceof works with classes because classes exist at runtime.

class User {
  constructor(public name: string) {}
}

const user = new User("Mihir");

console.log(user instanceof User);

This logs:

true

instanceof Does Not Work with Interfaces

Interfaces do not exist at runtime.

interface User {
  name: string;
}

You cannot do this:

value instanceof User;

User is only a TypeScript type, not a JavaScript runtime value.


Use Property Checks for Interfaces

For interface-like objects, use property checks or type guards.

interface User {
  name: string;
}

function isUser(value: unknown): value is User {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value
  );
}

Quick Recap

  • instanceof narrows class and constructor-based values.
  • It works well with Date, Error, and custom classes.
  • It requires runtime values.
  • It does not work with interfaces or type aliases.
  • Use property checks for plain objects.

Next up, we will learn Type Guards →.