Understanding TypeScript's Type Narrowing with Practical Examples

Learn how TypeScript's type narrowing helps catch errors early by refining variable types during coding, with simple examples for beginners.

TypeScript is a powerful tool that adds static types to JavaScript, helping developers catch errors before running their code. One of TypeScript's key features is type narrowing. Type narrowing means refining a variable's type into a more specific one based on conditions, making code safer and clearer.

Let's explore some practical examples to see how type narrowing works in TypeScript.

### Example 1: Narrowing with typeof

If a variable can be of multiple types, TypeScript allows you to narrow it down by checking its type using the typeof operator.

typescript
function printId(id: number | string) {
  if (typeof id === "string") {
    console.log(id.toUpperCase()); // Here id is treated as string
  } else {
    console.log(id); // Here id is treated as number
  }
}
printId("abc123");
printId(101);

In this example, TypeScript knows inside the if-block that `id` is a string, so it lets us call string methods like `toUpperCase()`. Outside that block, it treats `id` as a number.

### Example 2: Narrowing with instanceof

You can also narrow types using the `instanceof` operator to check if an object belongs to a specific class.

typescript
class Dog {
  bark() {
    console.log("Woof!");
  }
}
class Cat {
  meow() {
    console.log("Meow!");
  }
}

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark(); // Narrowed to Dog
  } else {
    animal.meow(); // Narrowed to Cat
  }
}

makeSound(new Dog());
makeSound(new Cat());

TypeScript knows the exact class of the object inside each condition, which allows safe access to methods specific to the class.

### Example 3: Narrowing with Equality Checks

Another way to narrow types is by comparing a variable to a specific value.

typescript
function printStatus(status: "success" | "error" | "loading") {
  if (status === "success") {
    console.log("Operation was successful!");
  } else if (status === "error") {
    console.log("There was an error.");
  } else {
    console.log("Loading...");
  }
}
printStatus("success");

Here, TypeScript narrows the `status` type inside each branch, helping prevent mistakes like calling methods that only exist on one string literal.

### Why is Type Narrowing Important?

Type narrowing reduces runtime errors by making sure you only use methods or properties valid for the currently known type. It also improves code readability and helps IDEs provide better suggestions.

### Summary

TypeScript's type narrowing is a beginner-friendly concept that improves your code safety. By using conditions like `typeof`, `instanceof`, or equality checks, you guide TypeScript in understanding your code better, which helps avoid common mistakes and bugs.