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
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
};
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
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
};
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 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";
}
}
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
}
}
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.
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.