ApiaryActive
Try: pause · settings · learn · wipe
← Community / Reading Room
TD
typescript · 3 min read

typescript discriminated unions

=====================================

=====================================

As a developer, you've likely encountered situations where you need to represent multiple possible values in a single variable. Maybe it's an object that can be either a User or an Admin, or perhaps it's a string that could be a Color value like "Red", "Green", or "Blue". In TypeScript, we have a powerful tool for handling such cases: discriminated unions.

What are Discriminated Unions?

A discriminated union is a type that represents a value that can be one of several possible types. The catch is that each variant (or value) must contain a discriminant – a specific property or value that makes it distinct from the others. This allows us to write more precise and maintainable code, reducing the likelihood of null pointer exceptions and other runtime errors.

Tagged Variants

Let's start with an example of tagged variants. Suppose we have a Payment type that can be either a Cash payment or a Card payment:

type Payment = {
  type: 'cash';
  amount: number;
} | {
  type: 'card';
  cardNumber: string;
  expirationDate: string;
}

In this example, the Payment type is a union of two objects. Each object has a type property that serves as the discriminant, indicating whether it's a cash or card payment.

Exhaustive Switch with Never Check

One of the benefits of discriminated unions is that we can use an exhaustive switch statement to handle each variant without worrying about null pointer exceptions. Here's how we might write a function to process payments:

function processPayment(payment: Payment): void {
  if (payment.type === 'cash') {
    console.log(`Processing ${payment.amount} cash payment`);
  } else if (payment.type === 'card') {
    console.log(`Processing card payment with number ${payment.cardNumber} and expiration date ${payment.expirationDate}`);
  } else {
    throw new Error('Unknown payment type');
  }
}

However, TypeScript allows us to use a more concise syntax using the never keyword:

function processPayment(payment: Payment): void {
  switch (payment.type) {
    case 'cash':
      console.log(`Processing ${payment.amount} cash payment`);
      break;
    case 'card':
      console.log(`Processing card payment with number ${payment.cardNumber} and expiration date ${payment.expirationDate}`);
      break;
    default:
      throw new Error('Unknown payment type');
  }
}

The never keyword indicates that the default branch should only be reached if all other branches are exhausted. This ensures that we've handled every possible variant of the union.

Concrete Example: Color Enum

Let's consider a more concrete example – an enum for colors:

enum Color {
  Red = 'Red',
  Green = 'Green',
  Blue = 'Blue'
}

We can then define a ColorValue type that's a discriminated union of the color values:

type ColorValue = {
  value: Color;
} | string;

This allows us to write functions like this:

function processColor(color: ColorValue): void {
  if (typeof color === 'string') {
    console.log(`Processing raw color value: ${color}`);
  } else {
    switch (color.value) {
      case Color.Red:
        console.log('Processing red color');
        break;
      case Color.Green:
        console.log('Processing green color');
        break;
      case Color.Blue:
        console.log('Processing blue color');
        break;
    }
  }
}

When NOT to Use Discriminated Unions

While discriminated unions are a powerful tool, there are cases where they might not be the best choice. For instance:

  • When dealing with large numbers of variants, the complexity and verbosity of the code can become overwhelming.
  • In situations where the discriminant is not unique or reliable, using a discriminated union may lead to more problems than it solves.

Related Apiary Lessons

If you're interested in learning more about TypeScript and its ecosystem, be sure to check out these related lessons:

Bee-themed One-Liner

"Discriminated unions: the nectar of precise and maintainable code."

Frequently asked
What is typescript discriminated unions about?
=====================================
References & sources
  1. Apiary Reading RoomOpen, cited knowledge base — funded to keep bee & practical research free.
From the Apiary Reading Room. Opinion & editorial — not financial advice. We don't overclaim.
More from the Reading Room