Intersection types in TypeScript are used when you want to combine multiple types into a single type. We make use of the & symbol to combine multiple types. For example,

type Animal = {
  name: string,
  height: number,
  weight: number,
};

type Nature = {
  friendly: boolean,
};

type Dog = Animal & Nature;

let dog: Dog = {
  name: "Bruno",
  height: 1,
  weight: 1,
  friendly: true,
};

console.log(dog); // {name: 'Bruno', height: 1, weight: 1, friendly: true}

In this example, we have a type Animal that has name, height, and weight properties. We also have a type Nature which has property friendly.

Now, we want to create a type Dog that has both the properties of Animal and Nature. We can do so by using & symbol which is used to insert types. So, we write

type Dog = Animal & Nature;

This way type Dog can have properties of both Animal and Nature.

Similarly, the intersection is also possible on interfaces. We can rewrite the above code using the interface and the code should still work.

interface Animal {
  name: string;
  height: number;
  weight: number;
}

interface Nature {
  friendly: boolean;
}

type Dog = Animal & Nature;

let dog: Dog = {
  name: "Bruno",
  height: 1,
  weight: 1,
  friendly: true,
};

console.log(dog); // {name: 'Bruno', height: 1, weight: 1, friendly: true}

In this case, both Animal and Nature are interfaces and still, we can combine them.

Let’s look at another example, where two interfaces have the same property name and type.

interface Animal {
  name: string;
  weight: number;
}

interface Dog {
  weight: number;
}

type Cat = Animal & Dog;

let cat: Cat = {
  name: "Meemo",
  weight: 1,
};

console.log(cat); // {name: 'Meemo', weight: 1}

In this case, we have the property weight of the type number in both interfaces. When we intersect both the interfaces for type Cat, an object of Cat will have a name and a weight property.

let cat: Cat = {
  name: "Meemo",
  weight: 1,
};

console.log(cat); // {name: 'Meemo', weight: 1}

What if the type of name property is different in both interfaces? What do you think will happen?

interface Animal {
  name: string;
  weight: string;
}

interface Dog {
  weight: number;
}

type Cat = Animal & Dog;

let cat1: Cat = {
  name: "Meemo",
  weight: 1, // ❌ Type 'number' is not assignable to type 'never'.
};

let cat2: Cat = {
  name: "Cookie",
  weight: "1", // ❌ Type 'string' is not assignable to type 'never'.
};

As you can see, there is no combinable type for string and number, hence, TypeScript will complain that you cannot assign it to type never.