In programming language theory, a discriminated union (also a sum type, or disjoint union) is a special kind of algebraic data type (ADT) that can hold a value of one among several different types. It also stores which type it currently holds.

Only one of the types can be in use at any one time, and a tag field explicitly indicates which type is in use. It can be thought of as a type that has several “cases”, each of which should be handled correctly when that type is manipulated.1

Discriminated unions carry certain safety properties. Accesses are safe (relative to C’s unions), and the compiler can enforce that all cases are handled (for pattern matching). The primary downside is that the tag requires space, which adds a small amount of memory overhead.

Language-specific

  • In Rust, discriminated unions are called enums.

In TypeScript

We must use a minor workaround to implement discriminated unions (à la Rust, where we have a tag and payload). First, we define a type with a tag member:

type NetworkLoadingState = {
  state: "loading";
};
 
type NetworkFailedState = {
  state: "failed";
  code: number;
};
 
type NetworkSuccessState = {
  state: "success";
  response: {
    title: string;
    duration: number;
    summary: string;
  };
};

Optionally, we can contain each tag within an enum for even more safety. Then, the discriminated union is described with a union type:

type NetworkState =
  | NetworkLoadingState
  | NetworkFailedState
  | NetworkSuccessState;

To pattern match on the type, we check the state variable inside a NetworkState object. The compiler is smart enough to infer which type after we’ve checked the tag.

Footnotes

  1. From the Wikipedia article on “Tagged union”.