Open In App

What are Discriminated union types?

Last Updated : 17 Jan, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In TypeScript, a discriminated union type is also called “tagged union types” or “algebraic data types”. It is a type that represents a value that can be one of several different types, with a tag indicating the type of the value. To create a discriminated union type in TypeScript, use the ‘|’ operator to combine the set of types. Here, the “discriminant” property can be used to distinguish between the different shapes of the object. 

A discriminated union type can be used to model a value that can be one of several different types, each of which has a different set of properties. For example, you might define a discriminated union-type Shape that can hold either a ‘Circle’ or a ‘Rectangle’, like this:

type Shape = Circle | Rectangle

interface Circle {
    kind: "circle";
    radius: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

The Shape type is a union of the Circle and Rectangle types, which means that a value of type Shape can be either a Circle or a Rectangle. You can then use this ‘Shape’ type to define a value that is either a circle with a given radius or a rectangle with a given width and height:

let s1: Shape = { kind: "circle", radius: 10 };
let s2: Shape = { kind: "rectangle", width: 5, height: 10 };

The kind of property is called a “discriminant”, because it is used to distinguish between the different types of discriminated unions. In this example, the kind property has the value “circle” for Circle values and the value “rectangle” for Rectangle values. You can use type guards to determine the type of a Shape value and extract its fields:

function getArea(s: Shape): number {
    if (s.kind === "circle") {
        return s.radius * s.radius * Math.PI;
    } else {
        return s.width * s.height;
    }
}

The type guard (s.kind === “circle”) checks the value of the kind property and narrows the type of s to Circle if the check succeeds. This allows you to access the radius property of s in the following line. 

You can also use the switch statement to perform pattern matching on a discriminated union type:

function getArea(s: Shape): number {
    switch (s.kind) {
        case "circle":
            return s.radius * s.radius * Math.PI;
        case "rectangle":
            return s.width * s.height;
    }
}

There are different types of discriminated union types that can be used in TypeScript.

1. Type-based discriminated unions: This discriminated union uses the types of values being combined as the discriminant. 

For example:

type Result<T> = { success: true, value: T } 
    | { success: false, error: string }

In this example, the ‘Result’ type is a discriminated union that has two alternatives: a success variant, which contains a ‘value’ field, and a ‘failure’ variant, which contains an ‘error’ field. The discriminant is the ‘success’ field, which is a boolean that indicates whether the result is a success or a failure.

2. String-based discriminated unions: This discriminated union uses string literals as the discriminant. 

For example:

type Action = 
  | { type: "increment" }
  | { type: "decrement" }
  | { type: "reset", value: number }

In this example, the ‘Action’ type is a discriminated union that has three alternatives: an ‘increment’ action, a ‘decrement’ action, and a ‘reset’ action. The discriminant is the ‘type’ field, which is a string literal that indicates the type of action.

3. Numeric-based discriminated unions: This discriminated union uses numeric literals as the discriminant. 

For example:

type NumberBase = 2 | 8 | 10 | 16
type Number = { base: NumberBase, value: string }

In this example, the ‘NumberBase’ type is a discriminated union that has four alternatives: base 2, base 8, base 10, and base 16. The ‘Number’ type is a record type that contains a ‘base’ field, which is a value of type ‘NumberBase’, and a ‘value’ field, which is a string that represents the number in the given base.

4. Mixed discriminated unions: This discriminated union uses a combination of different types as the discriminant.

For example:

type Tree<T> = 
  | { kind: "leaf", value: T }
  | { kind: "node", left: Tree<T>, right: Tree<T> }

In this example, the ‘Tree’ type is a discriminated union that has two alternatives: a ‘leaf’ node, which contains a single ‘value’ field, and a ‘node’ node, which contains a ‘left’ and a ‘right’ field, both of which are trees. The discriminant is the ‘kind’ field, which is a string literal that indicates the type of the node.

Overall, Discriminated unions is a powerful way to define complex data types and data structures in a type-safe way in TypeScript and can be used to model many different kinds of values and relationships. They can be especially useful when working with data that can have multiple shapes, such as data from a JSON API or a user interface with multiple input types.


Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads