runtime.boot

Full Stack Systems
Production Mindset

CodeWithMihir

Engineering thoughtful products from interface to infrastructure.

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 →.