Understanding TypeScript Union Types

Share with a friend:

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

  1. Use Descriptive Naming: When using union types, give them meaningful names to enhance code readability and maintainability.
  2. Consider Type Predicates: Leverage type guards like typeof and instanceof to narrow down types and ensure type safety.
  3. 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.
  4. Utilize Intersection Types: When appropriate, use intersection types to combine multiple types and create more complex data structures.
  5. 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.

Share with a friend:

Rajae Robinson

Rajae Robinson is a young Software Developer with over 3 years of work experience building websites and mobile apps. He has extensive experience with React.js and Next.js.

More Posts on Typescript