Union Type in TypeScript

TypeScript unions are a powerful feature that allows you to specify multiple possible types for a variable or function parameter. This is useful when you're not sure what type of data will be passed in or when you want to handle different types of data in the same way.

Creating Unions

You can create a union type by using the operator to combine multiple types. For example:

let age: string number; // age can be either a string or a number
let value: boolean string; // value can be either true/false or a string
let item: string number null; // item can be a string, a number, or null

Running the TypeScript compiler (tsc) will generate the following JavaScript code:

let age; // age can be either a string or a number let value; // value can be either true/false or a string let item; // item can be a string, a number, or null

Benefits of Using Unions

  1. Improved type safety: Unions help you ensure that only valid data is assigned to variables and function parameters. This can prevent errors and improve the overall quality of your code.
  2. More flexible code: Unions allow you to write code that can handle different types of data without using complex logic. This can make your code more concise and easier to maintain.
  3. Better documentation: Unions provide clear documentation for your code, making it easier for other developers to understand what types of data your code expects.

Function Parameters with Union Types

You can use unions to specify the different types of data that a function can accept.

function add(a: number string, b: number string): number string { if (typeof a === "number" && typeof b === "number") { return a + b; } else { return `${a}${b}`; } } const result1 = add(1, 2); // result1 is of type number const result2 = add("Hello", "World"); // result2 is of type string

Running the TypeScript compiler (tsc) will generate the following JavaScript code:

function add(a, b) { if (typeof a === "number" && typeof b === "number") { return a + b; } else { return `${a}${b}`; } } const result1 = add(1, 2); // result1 is of type number const result2 = add("Hello", "World"); // result2 is of type string

Variable declarations

You can use unions to declare a variable that can hold different types of data.

let option: string number boolean; if (Math.random() > 0.5) { option = "Hello"; } else { option = true; } if (typeof option === "string") { console.log(`Option is a string: ${option}`); } else if (typeof option === "number") { console.log(`Option is a number: ${option}`); } else { console.log(`Option is a boolean: ${option}`); }

Running the TypeScript compiler (tsc) will generate the following JavaScript code:

let option; if (Math.random() > 0.5) { option = "Hello"; } else { option = true; } if (typeof option === "string") { console.log(`Option is a string: ${option}`); } else if (typeof option === "number") { console.log(`Option is a number: ${option}`); } else { console.log(`Option is a boolean: ${option}`); }

Discriminated Unions

A discriminated union is a special type of union where each type has a unique property that can be used to distinguish it from the other types. This can be useful for narrowing down the type of a variable and improving type safety.

interface User { name: string; age: number; type: "user"; } interface Admin { name: string; permissions: string[]; type: "admin"; } type Person = User Admin; function getUserInfo(person: Person): string { if (person.type === "user") { return `User: ${person.name} - Age: ${person.age}`; } else if (person.type === "admin") { return `Admin: ${person.name} - Permissions: ${person.permissions.join(", ")}`; } // Default return statement return "Invalid person type"; }

Running the TypeScript compiler (tsc) will generate the following JavaScript code:

function getUserInfo(person) { if (person.type === "user") { return `User: ${person.name} - Age: ${person.age}`; } else if (person.type === "admin") { return `Admin: ${person.name} - Permissions: ${person.permissions.join(", ")}`; } // Default return statement return "Invalid person type"; }

Union Types in Interfaces

// Interface with a property having a union type interface Shape { area: number string; } // Objects implementing Shape can have area as number or string const square: Shape = { area: 16 }; const circle: Shape = { area: "πr^2" }; // const triangle: Shape = { area: true }; // Error: Type 'boolean' is not assignable to type 'number string'

Running the TypeScript compiler (tsc) will generate the following JavaScript code:

// Objects implementing Shape can have area as number or string const square = { area: 16 }; const circle = { area: "πr^2" }; // const triangle: Shape = { area: true }; // Error: Type 'boolean' is not assignable to type 'number string

Type Guards

Type guards help narrow down the possible types within a union.

// Function with type guard function displayType(value: number string): void { if (typeof value === "number") { console.log("It's a number: " + value); } else { console.log("It's a string: " + value); } } displayType(42); // Output: It's a number: 42 displayType("Hello"); // Output: It's a string: Hello // displayType(true); // No error, but it won't reach the "typeof" check

Running the TypeScript compiler (tsc) will generate the following JavaScript code:

// Function with type guard function displayType(value) { if (typeof value === "number") { console.log("It's a number: " + value); } else { console.log("It's a string: " + value); } } displayType(42); // Output: It's a number: 42 displayType("Hello"); // Output: It's a string: Hello // displayType(true); // No error, but it won't reach the "typeof" check

Type Aliases

Unions can be used with type aliases for cleaner code.

type ID = number string; function displayID(id: ID): void { console.log("ID: " + id); } displayID(123); // Valid displayID("abc"); // Valid // displayID(true); // Error: Type 'boolean' is not assignable to type 'ID'

In this example, ID is a type alias representing a union of number and string.

Running the TypeScript compiler (tsc) will generate the following JavaScript code:

"use strict"; function displayID(id) { console.log("ID: " + id); } displayID(123); // Valid displayID("abc"); // Valid // displayID(true); // Error: Type 'boolean' is not assignable to type 'ID'
Additional Notes:
  1. You can use unions with any type, including other unions and generics.
  2. TypeScript will automatically perform type narrowing based on the context, allowing you to use more specific types within your code.
  3. You can use the typeof operator to check the type of a variable at runtime.

Conclusion

TypeScript unions allow the definition of variables, function parameters, or object properties that can have values of multiple specified types. By using the union type operator ( |), developers can enhance flexibility while maintaining static typing and catching potential type errors during development.