Intersection and Union Types in TypeScript: When and How to Use Them

Author: Arreyettajnr
Reading Time: 3 min read
Published: Sep 12, 2024

TypeScript provides powerful type system features that allow developers to create complex and flexible type definitions. Two of the most useful and versatile of these features are intersection types and union types. In this article, we'll explore what these types are, when to use them, and how they can improve your TypeScript code.

Union Types

A Union type represents a value that can be one of several types. This is useful when a variable or function parameter can accept more than one type of value.

The syntax for a union type is really simple:

type A = string | number;

In the example above, A can be either a string or a number.

Union Types can be very useful in these circumstances:

  • You want to create a type that represents a set of literal values
  • You need to handle different cases in a type-safe manner
  • When a value can be one of several types but not a combination

For instance, imagine a function that takes either a string or number and performs an operation based on the type:

function newValue(value: string | number): void {
  if (typeof value === 'string') {
    console.log(`String: ${value.toUpperCase()}`);
  } else {
    console.log(`Number: ${value.toFixed(2)}`);
  }
}

newValue("hello");  // Output: String: HELLO
newValue(42.3);     // Output: Number: 42.30

Intersection Types

An Intersection type allows you to combine multiple types into one. When you use Intersection types, the result is a type that includes all the properties of the intersected types. They are defined using the & operator.

The syntax for intersection types looks like this:

type A = { name: string };
type B = { age: number };
type Person = A & B;

In this case, Person will have both name and age properties.

Intersection Types can be very useful in these circumstances:

  • When you need to combine multiple types into one type that has all the properties of both
  • When modeling objects that require properties from multiple sources
  • Combining interfaces or type aliases
  • Extending existing types with additional properties

Here's how to use Intersection types:

type Address = { city: string; country: string };
type Contact = { phone: string; email: string };

type Customer = Address & Contact;

const customer: Customer = {
  city: "New York",
  country: "USA",
  phone: "+1-234-567-890",
  email: "customer@example.com"
};

console.log(customer);

Here, customer must have all the properties from both Address and Contact.

Union Types vs. Intersection Types

Understanding the difference between Union and Intersection types is key to using them effectively.

  • Union types (A | B): A value can be of type A or type B but not both.
  • Intersection types (A & B): A value must satisfy both types A and B.

Example: Combining Union and Intersection Types

You can combine Union and Intersection types for more complex scenarios.

type Admin = { role: "admin"; accessLevel: number };
type User = { username: string; password: string };

type AdminOrUser = Admin | User;
type AdminAndUser = Admin & User;

function handleUser(user: AdminOrUser) {
  if ("accessLevel" in user) {
    console.log(`Admin with access level: ${user.accessLevel}`);
  } else {
    console.log(`User with username: ${user.username}`);
  }
}

const admin: AdminAndUser = {
  role: "admin",
  accessLevel: 5,
  username: "adminUser",
  password: "securePassword"
};

handleUser(admin); // Output: Admin with access level: 5

Conclusion

Union and Intersection types are fundamental concepts in TypeScript that enhance type safety and flexibility. Union types allow variables to be one of several types, while Intersection types combine multiple types into one. By leveraging these features, you can write more maintainable, expressive, and safer code.