TypeScript, a superset of JavaScript, adds static typing to the language, providing developers with the ability to catch type-related errors at compile time. One of the powerful features TypeScript offers is union types.
Union types allow a variable to have multiple types, providing flexibility in handling different data scenarios.
In this article, we’ll delve into TypeScript union types, their syntax, use cases, and best practices.
Introduction to Union Types
In TypeScript, union types allow us to specify that a variable can have multiple, different types. This means that a variable with a union type can hold values of any of those types. This provides flexibility and helps us write more versatile and robust code.
Consider a scenario where a function can accept either a string or a number. Instead of using any
(which would sacrifice type safety), we can use a union type to explicitly state that the parameter can be of either type.
Do you know the difference between any
and unknown
in Typescript? Visit this article to find out.
Syntax of Union Types
The syntax for defining a union type in TypeScript involves using the pipe |
operator between the types you want to combine. For example, to create a union type that allows a variable to be either a string or a number, you would write:
let myVar: string | number;
This tells TypeScript that myVar
can be either a string or a number, allowing us to assign values of both types to it.
Using Union Types
1. Function Parameters
Union types are commonly used when defining function parameters. Let’s say we have a function that accepts either a string or a number:
function printValue(value: string | number) {
console.log(value);
}
Now, we can call printValue
with either a string or a number:
printValue("Hello"); // Output: Hello
printValue(42); // Output: 42
2. Conditional Logic
Union types become particularly useful when dealing with conditional logic. Consider the following example:
function formatInput(input: string | number) {
if (typeof input === "string") {
return input.toUpperCase();
} else {
return input.toFixed(2);
}
}
In this example, the function formatInput
accepts a parameter that can be either a string or a number. Depending on the type, it performs a different operation. This kind of flexibility is invaluable in real-world applications.
Type Guards
TypeScript provides a feature called “type guards” that allows us to narrow down the possible types of a variable within a certain block of code. This is especially useful when dealing with union types.
Using typeof Type Guard
function formatInput(input: string | number) {
if (typeof input === "string") {
return input.toUpperCase();
} else {
return input.toFixed(2);
}
}
In the if
block, TypeScript knows that input
must be a string because of the typeof
check. This allows us to call string-specific methods like toUpperCase
without causing a compilation error.
Using instanceof
Type Guard
class Car {
drive() {
console.log("Driving...");
}
}
class Bike {
pedal() {
console.log("Pedaling...");
}
}
function moveVehicle(vehicle: Car | Bike) {
if (vehicle instanceof Car) {
vehicle.drive();
} else {
vehicle.pedal();
}
}
In this example, the moveVehicle
function accepts a parameter that can be either a Car
or a Bike
. Using the instanceof
operator, we can determine which type it is and call the appropriate method.
Intersection Types
While union types allow for flexibility, intersection types provide the ability to combine multiple types. An intersection type is denoted using the &
operator. For instance:
type Admin = {
name: string;
privileges: string[];
}
type Employee = {
name: string;
startDate: Date;
}
type ElevatedEmployee = Admin & Employee;
In this example, ElevatedEmployee
is an intersection type that combines the properties of both Admin
and Employee
.
Best Practices
- Use Descriptive Naming: When using union types, give them meaningful names to enhance code readability and maintainability.
- Consider Type Predicates: Leverage type guards like
typeof
andinstanceof
to narrow down types and ensure type safety. - Avoid Excessive Unions: While union types are powerful, having too many possible types can lead to complex code that’s harder to reason about. Use them judiciously.
- Utilize Intersection Types: When appropriate, use intersection types to combine multiple types and create more complex data structures.
- Document Union Types: If a function or variable can have a union type, document the possible types to aid other developers (or your future self) in understanding the code.
Conclusion
TypeScript’s union types are a crucial tool for writing flexible and type-safe code. By allowing variables to have multiple types, we can handle a wide range of scenarios with confidence.