Combining Types and Interfaces with & or | in TypeScript

Photo by Kevin Olson on Unsplash

Combining Types and Interfaces with & or | in TypeScript

Utilizing & or | for Combining Types in TypeScript

Today, I reviewed a piece of code where a variable could be of two types, but to enhance readability, the simple method is to use union and intersection.

In this example, we have defined two interfaces, Player and Basket, each representing a specific type.

interface Player {
  id: number,
  name: string
}

interface Basket {
  points: number,
  position: string
}

In the code, they were using both types in the declaration:

const player: Player & Basket = {
    id: 1,
    name: "lebron",
    points: 12,
    position: "foward"
  }

We can simplify the code using union and intersection, but what are the differences between both?

Union Types

Union types, uses the | operator, allows a variable to have multiple types. This means that a variable declared with a union type can hold values that belong to any of those types.

export type WNbaPlayer = Player | Basket

The WNbaPlayer type is a union of both interfaces. This allows us to create an object that can accept either a Player or a Basket object.

They are handy for scenarios where flexibility is required, such as handling different input data formats or user inputs.

Example:

const player3 : WNbaPlayer = {
  points: 3,
  position: "foward"
}
//valid object
const player4 : WNbaPlayer = {
  id: 3,
  name: "Irina"
}
//valid
const player5 : WNbaPlayer = {
    id: 1,
    name: "Irina",
    points: 33,
    position: "center"
  }
//valid

Next, let's proceed to intersection types.

Intersection Types

In contrast to union types, intersection types are denoted by the & operator and require a variable to have properties from all specified types.

export type NbaPlayer = Player & Basket

They are valuable when you need to create composite types by combining properties from multiple sources and for situations like creating rich data structures or merging objects with specific properties.

Example:



const player2 : NbaPlayer = {
    id: 1,
    name: "Irina",

  }
//invalid

const player3 : NbaPlayer = {
    points: 33,
    position: "center"
  }

//invalid
const player2 : NbaPlayer = {
    id: 1,
    name: "Irina",
    points: 33,
    position: "center"
  }

//!! Yeah is valid!

We create a NbaPlayer type that combines properties from both interfaces using an intersection type. As a result, the player3 object must have properties from both Player and Basket.

Extends Interfaces

Thanks to Jörgen de Groot , we have another alternative by using extended interfaces, it add extra properties to the interfaces.

interface PlayerExtend extends Player {
//.... new properties
}

The extended interface is good when the new properties are used only by the extended interface, for example:

  • If Basket interface cannot be used without the Player interface then extension make sense, and we don't need to create a useless type.
  • If Basket interface is used in other cases then use Intersection Types
interface PlayerExtended extends Player {
    position: string,
    points: string
}

Read more about extending types

Recap

To sum it up, it's super important to know when to use Union or Intersection Types if you want to write neat and easy-to-read code. Keep this in mind for your next challenge, and don't hesitate to check out the official Typescript documentation for a more in-depth look at the topic.

Feel free to play with the example code:

Have fun!