CodeWithMihir

TypeScript Tutorial

TypeScript Type Narrowing Explained

Learn how type narrowing works in TypeScript, including typeof checks, truthiness checks, equality checks, unions, and practical examples.

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

Type narrowing means TypeScript starts with a broad type and then understands a more specific type after a check.


The Problem

function printId(id: string | number) {
  console.log(id.toUpperCase());
}

This is invalid because number does not have toUpperCase.

Before using string-only methods, you must narrow the type.


Narrowing with typeof

function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id.toFixed(2));
  }
}

Inside the if block, TypeScript knows id is a string.

Inside the else block, TypeScript knows id is a number.


Narrowing with Truthiness

function printName(name: string | null) {
  if (name) {
    console.log(name.toUpperCase());
  }
}

Inside the if, TypeScript knows name is not null.


Narrowing with Equality

function handleStatus(status: "loading" | "success" | "error") {
  if (status === "success") {
    console.log("Show data");
  }
}

The comparison narrows status to the literal type "success" inside the block.


Narrowing with the in Operator

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

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

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

The in operator checks whether a property exists.


Narrowing with instanceof

function printDate(value: Date | string) {
  if (value instanceof Date) {
    console.log(value.toISOString());
  } else {
    console.log(value.toUpperCase());
  }
}

instanceof is useful for class instances and built-in objects like Date.


Control Flow Analysis

TypeScript follows your code flow.

function getLength(value: string | string[]) {
  if (typeof value === "string") {
    return value.length;
  }

  return value.length;
}

After the if returns, TypeScript understands the remaining code handles string[].


Quick Recap

  • Narrowing turns broad types into more specific types.
  • typeof is useful for primitives.
  • Truthiness checks remove values like null and undefined.
  • Equality checks narrow literal unions.
  • in and instanceof help with object unions.

Next up, we will go deeper into typeof Narrowing →.