Learn TypeScript: Unions and Intersection Types

You may want to read this article in Bahasa Indonesia version: Mengenal TypeScript: Union & Intersect pada Type
TypeScript offers a powerful and flexible data type system. Two important features of the TypeScript data type system are Union Type and Intersection Type. This article will explain both concepts with simple code examples.
Union Type
Union Type allows us to define variables that can accept more than one data type. Union Type is represented with a pipe(|
) symbol.
/** Define variable with Union Type */
let id: string | number;
/** Both assignments are valid */
id = "ABC123";
id = 456;
/** Error: Type boolean is not assignable to type string | number */
// id = true;
Using Union Type in Function
When working with Union Type, we can only access properties or methods that are available in all types involved.
function printId(id: string | number) {
/** Metode toString() available for both string and number */
console.log(id.toString());
/** Error: Property 'toUpperCase' does not exist on type 'string | number'.
// Property 'toUpperCase' does not exist on type 'number'.
// console.log(id.toUpperCase());
}
Type Narrowing
To access specific properties of one of the types, we need to perform type narrowing by checking the data type first.
function printId(id: string | number) {
if (typeof id === "string") {
// In this block, TypeScript knows that id is a string
console.log(id.toUpperCase());
} else {
// In this block, TypeScript knows that id is a number
console.log(id.toFixed(2));
}
}
Union Type with Arrays and Objects
Union Type can also be applied to arrays and objects.
/** Array that can contain string or number */
const data: (string | number)[] = ["satu", 2, "tiga", 4];
/** Object with properties that can be string or number */
type Product = {
id: string | number;
name: string;
price: number;
};
const product: Product = {
/** id can be string or number */
id: "P001",
name: "Laptop",
price: 15000000,
};
Intersection Type
Intersection type allows us to combine multiple types into one new type. Intersection Type is represented with ampersand(&
) symbol.
/** Define two types */
type Person = {
name: string;
age: number;
};
type Employee = {
employeeId: string;
department: string;
};
/** Combine two types with Intersection Type */
type EmployeePerson = Person & Employee;
/** Object must have all properties from both types */
const employee: EmployeePerson = {
name: "Budi",
age: 30,
employeeId: "E001",
department: "Engineering",
};
Usage Examples
Here are some examples of Union and Intersection Type usage.
Union Type for Function Overloading
/** Function that can receive string or array of string */
function formatText(input: string | string[]): string {
if (Array.isArray(input)) {
return input.join(", ");
}
return input;
}
console.log(formatText("Hello")); // Output: Hello
console.log(formatText(["Hello", "World"])); // Output: Hello, World
Creating a New Type from Several Types
type Loggable = {
log: (message: string) => void;
};
type Serializable = {
serialize: () => string;
};
/** Create a type that combines logging and serialization capabilities */
type LoggableAndSerializable = Loggable & Serializable;
class Logger implements LoggableAndSerializable {
log(message: string) {
console.log(`[LOG]: ${message}`);
}
serialize() {
return JSON.stringify(this);
}
}
Conclusion
Union Type and Intersection Type are powerful features in TypeScript that allow us to create a flexible yet secure type system. Union Type (|
) allows variables to accept multiple alternative data types, providing flexibility when needed. Meanwhile, Intersection Type (&
) combines multiple types into one new type that has all the properties of those types.
These two concepts become very useful tools in building complex applications while maintaining the data type safety that TypeScript is known for.