DEV Community

Michal M.
Michal M.

Posted on • Originally published at geodev.me

Discriminated unions in Typescript

Discriminated unions, also known as tagged unions or algebraic data types, are a feature in TypeScript that enable developers to model and work with varying types of data in a concise and type-safe manner. They provide a way to define a type that can represent multiple alternatives, where each alternative has a unique discriminant property to differentiate it from the others. This discriminant property acts as a type guard, allowing the TypeScript compiler to narrow down the type and provide more accurate type inference.

By using discriminated unions, developers can create expressive and self-describing types that accurately capture the possible states or variants of a value. This is particularly useful when dealing with scenarios such as handling different response types from API calls, representing different error conditions, or modeling complex data structures with varying shapes.

I would like to show how we can implement such types. I had a chance to implement it while working on a API architecture in one of my side-projects.
So we have an API response that might have two results: success and error (rejected state). Both response have some common fields, but they also differ.
If a request succeed we will receive a data in some kind of shape. If a request fails we will receive an error message.
First, let's type the generic API response:

type SuccessResponse<T> = { status: 'success', data: T, time: Date };
type ErrorResponse = { status: 'error', message: string, time: Date };
type RequestResponse<T> = SuccessResponse<T> | ErrorResponse;
Enter fullscreen mode Exit fullscreen mode

I used generic here to describe the data if we have a success request. Now let's create a mock response for each of the states.
At the top of the file I created a type for the specific data we're requesting.

type ResponseType = {
  users: string[];
  total: number;
};

const successResponse: RequestResponse<ResponseType> = {
  status: 'success',
  data: {
    users: ['Mark', 'Sophie', 'Pavel'],
    total: 3,
  },
  time: new Date(),
}

const errorResponse: RequestResponse<ResponseType> = {
  status: 'error',
  message: 'Comprehensive error message',
  time: new Date(),
}
Enter fullscreen mode Exit fullscreen mode

Depending on the status property, the appropriate branch of the conditional statement is executed, providing a clear and concise way to handle different response types.
Using discriminated unions in this scenario allows us to effectively handle different API response types with type safety and readability.

Top comments (0)