Union and intersection types in TypeScript
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 type
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.
Using union type in variables
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
variablewith the typestring | number. Here,string | numberdenotes a union type, meaning thevariablecan hold eitherstringornumbertype values.Lines 3–4: We assign the string
"Hello"to thevariableand log it to the console.Lines 6–7: Similarly, we assign the number
14to thevariableand 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'.
Using union in functions
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 parametervaluewith the typestring | number. Here,string | numberdenotes a union type, meaning that the parametervaluecan accept the values of eitherstringornumbertype. In this function, we call theconsole.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 typestring, and theprint_()function’s parametervalueaccepts bothstringandnumber, TypeScript infers the type ofvalueto be astringfor this invocation.Line 6: Similarly, we call the
print_()function with the number argument123. As123is of typenumber, and theprint_()function’s parametervalueaccepts bothstringandnumber, TypeScript infers the type ofvalueas anumberfor 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'.
Applications
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 type
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
namedinterface Interfaces in TypeScript are used to define the structure of objects. Car. It has two properties:makeandcolor, both of typestring.Lines 6–8: We declare another interface named
Electric. It has one property:batteryCapacity, which is of typenumber.Line 10: We create a new type named
ElectricCarusing an intersection type (&). It combines properties from theCarandElectricinterfaces, meaning objects of typeElectricCarmust have all properties defined in both interfaces.Lines 12–16: We declare a variable named
myCarof typeElectricCar. The object has properties ofmake,color, andbatteryCapacity, each with corresponding values.Line 18: Lastly, we log the value of
myCarto the console. The output string includes themake,color, andbatteryCapacityproperties with their respective values.
Applications
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.
Unions vs. intersection types
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. |
Conclusion
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