DEV Community

Rubén Alapont
Rubén Alapont

Posted on

Type Narrowing in TypeScript: A Journey through Type Inference

In this article, we'll explore the concept of type narrowing in TypeScript—a powerful technique that allows us to write more expressive and robust code.

What is Type Narrowing?

Type narrowing refers to the process of refining the type of a variable within a certain code block based on runtime checks. It enables us to work with more specific types and utilize their respective properties and methods, resulting in code that is both concise and less prone to runtime errors.

Let's dive into some complex examples to understand how type narrowing can significantly enhance our code.

Using Type Guards for Union Types

When dealing with union types, type guards allow us to narrow down the type of a variable based on specific conditions. Consider the following example:

type Shape = Square | Circle;

interface Square {
  kind: 'square';
  size: number;
}

interface Circle {
  kind: 'circle';
  radius: number;
}

function getArea(shape: Shape): number {
  if (shape.kind === 'square') {
    return shape.size ** 2;
  } else {
    return Math.PI * shape.radius ** 2;
  }
}
Enter fullscreen mode Exit fullscreen mode

In the above example, the getArea function accepts a Shape parameter that can be either a Square or a Circle. By using the kind property as a type guard, we can determine the specific shape and perform the appropriate calculations. This ensures type safety and enables us to access the relevant properties without the need for type assertions.

Narrowing Types with Type Predicates

Type predicates allow us to create custom functions that act as type guards. These functions assert the type of a variable and return a boolean value. Consider the following example:

interface User {
  id: number;
  name: string;
  isAdmin: boolean;
}

function isAdminUser(user: User): user is AdminUser {
  return user.isAdmin;
}

function processUser(user: User) {
  if (isAdminUser(user)) {
    console.log(`${user.name} is an admin user.`);
    // Additional admin-specific logic...
  } else {
    console.log(`${user.name} is a regular user.`);
    // Additional regular user logic...
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example, the isAdminUser function acts as a type predicate by asserting that the user is of type AdminUser based on the isAdmin property. By using this type guard, we can perform different operations and apply specific logic depending on whether the user is an admin or a regular user.

Narrowing Types with Typeof and Instanceof

Type narrowing can also be achieved using the typeof and instanceof operators. These operators allow us to check the type of a variable at runtime and narrow down its type accordingly.

function processValue(value: string | number) {
  if (typeof value === 'string') {
    console.log(`The length of the string is ${value.length}.`);
  } else if (typeof value === 'number') {
    console.log(`The value is a number: ${value}.`);
  }
}

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

class Dog extends Animal {
  woof() {
    console.log('Woof!');
  }
}

function processAnimal(animal: Animal) {
  if (animal instanceof Dog) {
    animal.woof();
  } else {
    console.log(`The animal ${animal.name} is not a dog.`);
  }
}
Enter fullscreen mode Exit fullscreen mode

In the above examples, we use typeof to check if thevalue is a string or a number and perform operations accordingly. Similarly, instanceof allows us to determine if an object is an instance of a particular class, enabling us to call specific methods or access class-specific properties.

Conclusion

Type narrowing in TypeScript is a valuable technique that empowers us to write more expressive and robust code. By utilizing type guards, type predicates, typeof, and instanceof, we can confidently narrow down the types of variables within our code blocks, leading to improved type safety and enhanced code quality.

Keep in mind that type narrowing should be used judiciously, and it's essential to strike a balance between code readability and type correctness.

Stay tuned for more articles on advanced TypeScript concepts and best practices. Happy coding, fellow developers!

Resources:

Note: The examples provided in this article are written in TypeScript, which is a statically typed superset of JavaScript. To use these features, it's recommended to have a basic understanding of TypeScript.

Top comments (0)