TypeScript Union and Intersection Types Explained
TypeScript is a statically typed superset of JavaScript that brings a wide range of type - related features to the table. Among these, union and intersection types are powerful tools that allow developers to create more flexible and precise types. Union types enable a variable to hold values of multiple different types, while intersection types combine multiple types into one, requiring a value to satisfy all the specified types. In this blog post, we will explore the fundamental concepts, usage methods, common practices, and best practices of TypeScript union and intersection types.
Table of Contents
Fundamental Concepts
Union Types
A union type in TypeScript is a way to specify that a variable can have one of several types. It is denoted using the | symbol. For example, if you have a variable that can be either a string or a number, you can define its type as string | number.
let value: string | number;
value = "hello"; // valid
value = 123; // valid
// value = true; // invalid
Intersection Types
An intersection type combines multiple types into one. A variable of an intersection type must satisfy all the types involved. It is denoted using the & symbol. Suppose you have two types TypeA and TypeB, and you create an intersection type TypeA & TypeB. A value of this intersection type will have all the properties and methods of both TypeA and TypeB.
type TypeA = {
propA: string;
};
type TypeB = {
propB: number;
};
type CombinedType = TypeA & TypeB;
let obj: CombinedType = {
propA: "test",
propB: 10
};
Usage Methods
Using Union Types
Union types are useful when a function can accept different types of input. For example, a function that can format either a number or a string.
function formatValue(value: string | number) {
if (typeof value === 'string') {
return value.toUpperCase();
} else {
return value.toFixed(2);
}
}
console.log(formatValue("hello")); // HELLO
console.log(formatValue(3.14159)); // 3.14
Using Intersection Types
Intersection types are often used when you want to create a new type that has the features of multiple existing types. Consider two types representing different aspects of a user, and you want to create a new type that combines them.
type UserInfo = {
name: string;
age: number;
};
type UserPermissions = {
canEdit: boolean;
canDelete: boolean;
};
type FullUser = UserInfo & UserPermissions;
let user: FullUser = {
name: "John",
age: 30,
canEdit: true,
canDelete: false
};
Common Practices
Union for Optional Values
Unions can be used to represent optional values. For instance, a function parameter can be either a specific type or undefined.
function printMessage(message: string | undefined) {
if (message) {
console.log(message);
} else {
console.log("No message provided");
}
}
printMessage("Hello!");
printMessage(undefined);
Intersection for Mixins
Intersection types are great for creating mixins. A mixin is a class or object that can be combined with other classes or objects to add additional functionality.
type Loggable = {
log: () => void;
};
type Serializable = {
serialize: () => string;
};
type LoggableAndSerializable = Loggable & Serializable;
class Logger implements Loggable {
log() {
console.log("Logging...");
}
}
class Serializer implements Serializable {
serialize() {
return "Serialized data";
}
}
class MyClass implements LoggableAndSerializable {
log() {
console.log("Custom logging");
}
serialize() {
return "Custom serialized data";
}
}
Best Practices
Type Guards for Unions
When working with union types, it’s important to use type guards to narrow down the type within a conditional block. TypeScript will then have more information about the actual type of the variable.
function getLength(value: string | number[]) {
if (typeof value === 'string') {
return value.length; // TypeScript knows value is a string here
} else {
return value.length; // TypeScript knows value is a number[] here
}
}
Avoid Over - Complex Intersections
Intersection types can quickly become complex and hard to manage if you combine too many types. Try to keep your intersections simple and focused on a specific purpose. If an intersection becomes too large, it might be a sign that you need to refactor your types.
Conclusion
TypeScript union and intersection types are powerful features that enhance the type - safety and flexibility of your code. Union types allow variables to hold values of multiple types, which is useful for handling different input scenarios. Intersection types enable you to combine multiple types, creating new types that have the features of all the involved types. By understanding the fundamental concepts, usage methods, common practices, and best practices, you can make the most of these features in your TypeScript projects.