TypeScript Union and Intersection Types
TypeScript provides powerful type system features that help you write safer and more predictable code. Among these features are union and intersection types, which offer flexibility in defining and managing complex types. This article introduces these concepts and provides examples to illustrate their usage.
What Are Union Types?
Union types allow a variable to hold values of different types. This is useful when you need to represent a value that could be one of several types. Union types are denoted using the |
(pipe) symbol.
Defining Union Types
To define a union type, you specify multiple types separated by the |
symbol:
let value: string | number;
value = "Hello, TypeScript"; // Valid
value = 42; // Valid
value = true; // Error: Type 'boolean' is not assignable to type 'string | number'
In this example, the variable value
can be either a string
or a number
, but not a boolean
.
Using Union Types in Functions
Union types are particularly useful in functions where parameters or return types can be multiple types:
function formatValue(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
} else {
return value.toFixed(2);
}
}
console.log(formatValue("hello")); // Output: HELLO
console.log(formatValue(123.456)); // Output: 123.46
The formatValue
function takes a parameter that can be either a string
or a number
and formats it accordingly.
What Are Intersection Types?
Intersection types allow you to combine multiple types into one. This means that a value of the intersection type will satisfy all the types in the intersection. Intersection types are denoted using the &
(ampersand) symbol.
Defining Intersection Types
To define an intersection type, you specify multiple types separated by the &
symbol:
interface Person {
name: string;
}
interface Employee {
employeeId: number;
}
type EmployeePerson = Person & Employee;
const john: EmployeePerson = {
name: "John Doe",
employeeId: 1234
};
console.log(john.name); // Output: John Doe
console.log(john.employeeId); // Output: 1234
In this example, the EmployeePerson
type combines the Person
and Employee
interfaces, resulting in a type that has both name
and employeeId
properties.
Using Intersection Types in Functions
Intersection types can also be used in functions to require multiple type properties:
function printEmployeeDetails(employee: Person & Employee): void {
console.log(`Name: ${employee.name}`);
console.log(`Employee ID: ${employee.employeeId}`);
}
const jane: EmployeePerson = {
name: "Jane Smith",
employeeId: 5678
};
printEmployeeDetails(jane);
// Output:
// Name: Jane Smith
// Employee ID: 5678
The printEmployeeDetails
function requires an argument that satisfies both the Person
and Employee
types.
Combining Union and Intersection Types
You can combine union and intersection types to create complex type definitions:
type Shape = Circle | Rectangle;
interface Circle {
kind: "circle";
radius: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
function getArea(shape: Shape): number {
if (shape.kind === "circle") {
return Math.PI * shape.radius * shape.radius;
} else {
return shape.width * shape.height;
}
}
const myCircle: Circle = { kind: "circle", radius: 10 };
const myRectangle: Rectangle = { kind: "rectangle", width: 20, height: 30 };
console.log(getArea(myCircle)); // Output: 314.159...
console.log(getArea(myRectangle)); // Output: 600
In this example, the Shape
type is a union of Circle
and Rectangle
, and the getArea
function handles both types accordingly.
Conclusion
Union and intersection types in TypeScript provide powerful ways to manage and combine types, offering flexibility and precision in type definitions. Union types allow a variable to be one of several types, while intersection types combine multiple types into one. By using these features, you can create more robust and type-safe applications.
Practice using union and intersection types to get comfortable with their capabilities and improve your TypeScript coding skills.