Combining Types and Interfaces with & or | 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 | BasketThe 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"
}
//validNext, 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 & BasketThey 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
Basketinterface cannot be used without thePlayerinterface then extension make sense, and we don't need to create a useless type. -
If
Basketinterface 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!
Real Software. Real Lessons.
I share the lessons I learned the hard way, so you can either avoid them or be ready when they happen.
No spam ever. Unsubscribe at any time.