Computer scienceProgramming languagesTypeScriptData TypesThinking of types as sets of values

Intersection types

3 minutes read

When working with TypeScript, you might need to combine multiple types into one. How would you do that? By using the intersection types. In this topic, you will learn about the intersection types; specifically, how to create and use them in your TypeScript programs.

What are intersection types?

Intersection types are a way in TypeScript to combine multiple types into one. This allows you to mix multiple types together to create a new type that includes all the properties of the original types. In other words, an intersection type is a type that combines multiple types into one, effectively combining them. For example, Name & Age & Height is a type that is all of Name and Age and Height. That means an object of this type will have all members of all three types. The syntax for creating intersection types is quite simple. You use the & operator between two or more types.

Here's an example:

type Name = { name: string };
type Age = { age: number };
type Height = { height: number };
type Person = Name & Age & Height;

let person: Person = {
    name: 'John',
    age: 25,
    height: 185
};

In this example, Person is an intersection type that includes Name and Age and Height. This means a variable of type Person must have a name property (from Name) and an age property (from Age) and a height property (from Height). It's worth noting that intersection types aren't limited to just two types. You can combine as many types as you want.

This feature is particularly useful when you want to create a type that can have properties from multiple other types. In other words, it allows you to create a type that is a "mixture" of other types.

Combining basic types

Let's say we have two types, one representing a Student and another representing an Employee. A Student has a grade property and an Employee has a salary property:

type Student = {
  grade: number;
};

type Employee = {
  salary: number;
};

Now, we want to create a type to represent a StudentEmployee, a person who is both a student and an employee. We can do this using an intersection type:

type StudentEmployee = Student & Employee;

let person: StudentEmployee = {
  grade: 90,
  salary: 30000,
};

Here, StudentEmployee is a type that includes both the grade property from Student and the salary property from Employee.

Combining object types with function types

Let's say we have a type Named that represents an object with a name property, and a type Runnable that represents a function that can be run.

type Named = {
  name: string;
};

type Runnable = {
  run: () => void;
};

We want to create a type NamedRunnable that represents a function that can be run and also has a name property. We can do this using an intersection type:

type NamedRunnable = Named & Runnable;

let myFunction: NamedRunnable = {
  name: 'myFunction',
  run: () => console.log('Running...'),
};

Here, NamedRunnable is a type that includes both the name property from Named and the run function from Runnable.

Differences between union types

Union types and intersection types in TypeScript are both ways to combine types, but they do so in different ways and for different reasons:

  • Union Types (TypeA | TypeB): A union type is a type that can be any one of several types. This means that an object that is of a union type can be assigned a value that is of any of the types in the union. For example, if we have type StringOrNumber = string | number;, a variable of type StringOrNumber can be either a string or a number.

  • Intersection Types (TypeA & TypeB): An intersection type is a type that combines multiple types into one, effectively intersecting the types together. This means that an object of an intersection type must have all the properties of all the types in the intersection. For example, if we have type NameAndAge = Name & Age;, a variable of type NameAndAge must have all the properties of Name and all the properties of Age.

So, union types are about adding more possible types to a variable (this OR that), while intersection types are about merging types together and combining their properties (this AND that).

Using with interfaces

You can use intersection types with interfaces in TypeScript. The process is very similar to using intersection types with type aliases. Here's an example:

interface Name {
  name: string;
}

interface Age {
  age: number;
}

type Person = Name & Age;

let person: Person = {
  name: 'John',
  age: 25
};

In this example, Person is an intersection type that includes both Name and Age. This means a variable of type Person must have both a name property (from Name) and an age property (from Age).

It's worth noting that you can achieve similar results with interface extension in TypeScript:

interface Name {
  name: string;
}

interface Age {
  age: number;
}

interface Person extends Name, Age {}

let person: Person = {
  name: 'John',
  age: 25
};

In this case, Person is an interface that extends both Name and Age, effectively creating an intersection of the two interfaces. This is another way to combine interfaces in TypeScript and is often used in place of intersection types when working with interfaces.

Example use case of intersection types

You could define a base type for your API responses that includes the properties for error handling. Then, for each specific API response, you could define a type that includes the specific properties for that response. You can then use intersection types to combine the base type with the specific type for each response.

Here's a simplified example:

type ApiErrorResponse = {
  error: boolean;
  errorMessage?: string;
};

type UserResponse = {
  user: {
    id: string;
    name: string;
    email: string;
  };
};

type UserApiResponse = ApiErrorResponse & UserResponse;

// Now, UserApiResponse includes both the error handling properties and the user properties.

let response: UserApiResponse = {
  error: false,
  user: {
    id: '1',
    name: 'John Doe',
    email: '[email protected]',
  }
};

In this example, UserApiResponse is an intersection type that includes both the error handling properties from ApiErrorResponse and the user properties from UserResponse. This way, you can handle both the data and potential errors in a consistent and type-safe way.

Conclusion

Intersection types in TypeScript allow you to create a new type that combines multiple existing types. This is done using the & operator. An intersection type TypeA & TypeB & TypeC is a type that includes all the properties of TypeA, TypeB, and TypeC. This feature is useful when you want an object to have properties from multiple types, effectively creating a "mixture" of those types. It can be applied to basic types, object types, function types, and interfaces, providing a way to extend and combine types in TypeScript.

How did you like the theory?
Report a typo