CodeWithMihir

TypeScript Tutorial

TypeScript in Operator Narrowing Explained

Learn how the in operator narrows object union types in TypeScript, with practical examples, optional properties, and common mistakes.

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

The in operator checks whether a property exists in an object.


The Problem

type Admin = {
  name: string;
  permissions: string[];
};

type Customer = {
  name: string;
  orders: number;
};

function describeUser(user: Admin | Customer) {
  console.log(user.permissions);
}

This is invalid because Customer does not have permissions.


Basic in Narrowing

function describeUser(user: Admin | Customer) {
  if ("permissions" in user) {
    console.log(user.permissions);
  } else {
    console.log(user.orders);
  }
}

Inside the if, TypeScript knows user is Admin.

Inside the else, TypeScript knows user is Customer.


Another Example

type EmailNotification = {
  email: string;
  subject: string;
};

type SmsNotification = {
  phone: string;
  message: string;
};

function send(notification: EmailNotification | SmsNotification) {
  if ("email" in notification) {
    console.log(notification.subject);
  } else {
    console.log(notification.phone);
  }
}

The property check tells TypeScript which object shape you have.


Works Best with Unique Properties

The in operator is most useful when each union member has a unique property.

type Cat = {
  meow: () => void;
};

type Dog = {
  bark: () => void;
};

function speak(pet: Cat | Dog) {
  if ("meow" in pet) {
    pet.meow();
  } else {
    pet.bark();
  }
}

Optional Properties

Be careful with optional properties.

type User = {
  name: string;
  email?: string;
};

function hasEmail(user: User) {
  if ("email" in user) {
    console.log(user.email);
  }
}

The property may exist, but its value can still be undefined.

If you need a real string, check the value too.

if (typeof user.email === "string") {
  console.log(user.email.toLowerCase());
}

in Operator with Unknown Objects

When using unknown, first make sure the value is an object.

function printId(value: unknown) {
  if (typeof value === "object" && value !== null && "id" in value) {
    console.log(value.id);
  }
}

The value !== null check is important because typeof null is "object".


Quick Recap

  • The in operator checks whether a property exists.
  • It is useful for narrowing object unions.
  • It works best with unique properties.
  • Optional properties may still have undefined values.
  • With unknown, check for object and non-null first.

Next up, we will learn instanceof Narrowing →.