TypeScript introduces powerful type features that help developers catch errors early in the development process and improve code maintainability. Two important concepts in TypeScript’s type system are union and intersection types. These concepts allow developers to create more flexible and precise type definitions. Let’s discuss them in detail.
Union types in TypeScript allow a variable to have multiple types. This means that a variable declared with a union type can store values of any type. Union types are denoted using the pipe (|
) symbol between type names. It is particularly useful when a function or variable can accept multiple values.
Let’s have an example of using unions with variables in the following playground:
let variable: string | number;variable = "Hello";console.log(variable); // Output: Hellovariable = 14;console.log(variable); // Output: 14
In the code above:
Line 1: We declare a variable named variable
with the type string | number
. Here, string | number
denotes a union type, meaning the variable
can hold either string
or number
type values.
Lines 3–4: We assign the string "Hello"
to the variable
and log it to the console.
Lines 6–7: Similarly, we assign the number 14
to the variable
and log it to the console.
Let’s see what happens if we assign some other value to the variable
:
let variable: string | number;variable = true; // error TS2322: Type 'boolean' is not assignable to type 'string | number'.console.log(variable);
In the code above, we try to assign a boolean value to the variable
on line 3, which is incompatible with the declared variable
type. Therefore, we encountered this error: index.ts(3,1): error TS2322: Type 'boolean' is not assignable to type 'string | number'.
Similarly, we can use unions with the function parameters. Let’s have an example of it in the following playground:
function print_(value: string | number) {console.log(value);}print_("Hello"); // Output: Helloprint_(123); // Output: 123
In the code above:
Lines 1–3: We define the print_()
function that takes a parameter value
with the type string | number
. Here, string | number
denotes a union type, meaning that the parameter value
can accept the values of either string
or number
type. In this function, we call the console.log()
method to print the value to the console.
Line 5: We call the print_()
function with the string argument "Hello"
. As "Hello"
is of type string
, and the print_()
function’s parameter value
accepts both string
and number
, TypeScript infers the type of value
to be a string
for this invocation.
Line 6: Similarly, we call the print_()
function with the number argument 123
. As 123
is of type number
, and the print_()
function’s parameter value
accepts both string
and number
, TypeScript infers the type of value
as a number
for this invocation.
Let’s see what happens if we pass some other value to the print_()
function:
function print_(value: string | number) {console.log(value);}print_(true); // error TS2345: Argument of type 'boolean' is not assignable to parameter of type 'string | number'.
In the code above, we try to pass a boolean value to the print_()
function on line 5, which is not compatible with the declared type of value
parameter. Therefore, we encountered this error: index.ts(5,8): error TS2345: Argument of type 'boolean' is not assignable to parameter of type 'string | number'.
Unions are used in various real-life applications. Some use cases are as follows:
They allow different data types of a function parameter, such as “string,” “number,” etc., so that the same function can process different types of inputs.
Optional parameters can be implemented using unions representing values that might be present or absent (null or undefined).
They are used to represent different system statuses, such as “To Do,” “In Progress,” “Done,” etc.
They are also used to handle different types of responses to an API call, such as “Success,” “Error,” etc.
Intersection types allow developers to combine multiple types into one. An intersection type is denoted using the ampersand (&
) symbol between type names. It is particularly useful when we combine properties or behaviors from multiple types, creating a new type encompassing all the combined features.
Let’s have an example of using intersection type in the following playground:
interface Car {make: string;color: string;}interface Electric {batteryCapacity: number;}type ElectricCar = Car & Electric;const myCar: ElectricCar = {make: "Tesla",color: "Black",batteryCapacity: 75};console.log("myCar:", myCar); // Output: myCar: { make: 'Tesla', color: 'Black', batteryCapacity: 75 }
In the code above:
Lines 1–4: We declare an Car
. It has two properties: make
and color
, both of type string
.
Lines 6–8: We declare another interface named Electric
. It has one property: batteryCapacity
, which is of type number
.
Line 10: We create a new type named ElectricCar
using an intersection type (&
). It combines properties from the Car
and Electric
interfaces, meaning objects of type ElectricCar
must have all properties defined in both interfaces.
Lines 12–16: We declare a variable named myCar
of type ElectricCar
. The object has properties of make
, color
, and batteryCapacity
, each with corresponding values.
Line 18: Lastly, we log the value of myCar
to the console. The output string includes the make
, color
, and batteryCapacity
properties with their respective values.
Intersections are used in various real-life applications. Some use cases are as follows:
They are used to enforce consistent data structures to guarantee the properties of all intersected data structures.
They are used to combine the roles and rights. Different roles can be combined with their rights and privileges to ensure the application’s security.
The union types provide flexibility by accepting values of multiple types. The intersection types combine properties from multiple types into a single type. In other words, union types represent “or” relationships, while intersection types represent “and” relationships. Let’s compare both in the table below:
Feature | Union Type | Intersection Type |
Usage | Union allows a variable to hold values of multiple types. | Intersection combines properties from multiple types into one. |
Relationships | It represents the “or” relationship between types. | It represents the “and” relationship between types. |
Syntax | It is defined using the pipe ( | It is defined using the ampersand ( |
Compatibility | The variable can hold any value that matches one of the specified types. | The variable must satisfy all the individual types’ requirements. |
Versatility | It enhances the versatility of functions and variables. | It creates a new type by combining properties from existing types. |
Union and intersection types are powerful features in TypeScript that enable developers to create more flexible and precise type definitions. Using union types, variables, and functions can accept values of multiple types, providing flexibility without sacrificing type safety. Intersection types allow for combining properties from multiple types into one, enabling objects to satisfy multiple contracts or have combined functionality. Understanding and effectively using union and intersection types can greatly enhance the development experience and code quality in TypeScript projects.
Free Resources