Typescript Union and Intersection types

TypeScript offers powerful mechanisms for composing complex types using union and intersection types. This chapter delves into the world of union and intersection types, exploring their functionalities, usage patterns, and how they enhance type safety and code expressiveness in your TypeScript applications.

Union Types

Representing Multiple Possible Typese

Union types allow you to define a variable that can hold one of several types. This is useful when a variable can have different values of specific types at different times in your code.

				
					type LoginState = "pending" | "success" | "failed";

let loginStatus: LoginState;

loginStatus = "pending"; // Valid
console.log(loginStatus); // Output: "pending"

loginStatus = "success"; // Also valid
console.log(loginStatus); // Output: "success"

// loginStatus = "invalid"; // Error: "invalid" is not a valid LoginState

				
			

Using the Pipe ( | ) Operator

The pipe symbol (|) is used to separate the different types within a union type definition.

				
					type Product = { name: string; price: number } | string;

let product: Product;

product = { name: "Hat", price: 15 }; // Valid (object type)
console.log(product.name); // Output: "Hat"

product = "T-Shirt"; // Also valid (string type)
console.log(product.length); // Error: 'length' does not exist on string type in 'product'

// Consider type guards or checks before accessing properties to handle both types safely

				
			

Intersection Types

Combining Multiple Types

Intersection types allow you to create a new type that incorporates the properties of multiple existing types. This is useful when you need a variable to have characteristics from several different types.

				
					interface User {
  name: string;
  age: number;
}

interface Address {
  street: string;
  city: string;
}

type UserWithAddress = User & Address;

const user: UserWithAddress = {
  name: "Alice",
  age: 30,
  street: "Main Street",
  city: "New York"
};

console.log(user.name, user.street); // Output: "Alice Main Street"

				
			

Using the Ampersand ( & ) Operator

The ampersand symbol (&) is used to combine multiple types within an intersection type definition.

				
					type SecuredString = string & { isEncrypted: boolean };

const password: SecuredString = "secret123" as SecuredString; // Type assertion for clarity
// password.isEncrypted; // Error: 'isEncrypted' does not exist on type 'string'

// Use with custom logic (if applicable) to ensure the secured string has the isEncrypted property
function encryptString(str: string): SecuredString {
  // ... encryption logic
  return { value: str, isEncrypted: true };
}

const encryptedPassword = encryptString(password);
console.log(encryptedPassword.isEncrypted); // Output: true (assuming encryption logic sets it)

				
			

When to Use Union and Intersection Types

Union Types

  • Representing variables with potential values of different specific types.
  • Modeling function return types that can be one of several types.
  • Defining discriminated unions (explained later) for improved type safety with conditional logic.

Intersection Types

  • Creating new types that combine properties from existing interfaces or types.
  • Specifying requirements for objects that must have characteristics from multiple types.
  • Enhancing type safety and code clarity by explicitly defining the expected properties.

Best Practices for Union and Intersection Types

  • Clarity and Readability: Use clear and descriptive names for your union and intersection types to reflect their purpose.
  • Exhaustiveness (Union Types): When using union types, strive for exhaustiveness in conditional checks to handle all possible type cases within your code.
  • Type Guards (Union Types): Consider using type guards with union types to refine the actual type at runtime and ensure safe access to properties.

Advanced Union Types: Discriminated Unions

Combining Types with a Shared Property

Discriminated unions are a special kind of union type where all member types share a common property that uniquely identifies their type. This allows for more robust type safety and conditional logic based on the discriminator property.

				
					interface LoginSuccess {
  type: "success";
  user: User; // User interface defined elsewhere
}

interface LoginFailed {
  type: "failed";
  error: string;
}

type LoginResult = LoginSuccess | LoginFailed;

function handleLogin(result: LoginResult) {
  if (result.type === "success") {
    console.log("Login successful! Welcome,", result.user.name);
  } else {
    console.error("Login failed:", result.error);
  }
}

const successfulLogin: LoginResult = { type: "success", user: { name: "Alice" } };
handleLogin(successfulLogin); // Output: "Login successful! Welcome, Alice"

const failedLogin: LoginResult = { type: "failed", error: "Invalid username or password" };
handleLogin(failedLogin); // Output: "Login failed: Invalid username or password"

				
			

Benefits of Discriminated Unions

  • Improved Type Safety: The discriminator property ensures type safety as you can only access properties based on the specific type within the union.
  • Clear Conditional Logic: Conditional checks using the discriminator property make code more readable and easier to maintain.

Experiments

  • Generics with Unions and Intersections: Explore how generics can be combined with unions and intersections to create even more flexible and reusable type definitions.
  • Mapped Types: Consider using mapped types, a powerful feature in conjunction with unions and intersections, to create new types based on the structure of existing types.

Union and intersection types are fundamental tools in TypeScript for defining complex data structures and function behaviors. By understanding their capabilities, limitations, and best practices, you can leverage them effectively to write expressive, type-safe, and well-structured TypeScript code. Remember to choose the appropriate type based on your needs and prioritize code clarity when using unions and intersections. Happy coding !❤️

Table of Contents

Contact here

Copyright © 2025 Diginode

Made with ❤️ in India