Skip to content

The Power of Discriminating Unions

Posted on:June 12, 2023

Here is the link for the firs part Conditional Typing in TypeScript

Table of contents

Open Table of contents

The Prolem

Imagine you have an APIResponse type that accommodates loading, success and error responses.

type APIResponse = {
  state: "loading" | "success" | "error";
  data?: { name: string; age: number };
  errorMessage?: string;
};

The APIResponse type attempts to cover loading, success, and error states within a single type. This leads to ambiguity and errors, making it challenging to write robust TypeScript code.

Using Discriminating Union

In TypeScript, discriminated unions are like a superhero move, letting developers create types that can handle different situations. They use discriminators, little markers for each situation, to make sure everything is managed smoothly. However, there’s a catch explored in the script: discriminated unions can only work their magic with specific values, not with generic types. So, while these unions are superheroic in handling known situations, they might need a sidekick when it comes to dealing with more generic stuff in TypeScript.

We can convert the APIResponse type into a discriminated union using the following approach

type LoadingState = {
  state: "loading";
};

type SuccessState = {
  state: "success";
  data: { name: string; age: number };
};

type ErrorState = {
  state: "error";
  errorMessage: string;
};

type APIResponse = LoadingState | SuccessState | ErrorState;

Now, each possible state of the API response has its own type, making it a discriminated union. The state property serves as the discriminator, ensuring that TypeScript can infer and check the specific shape of the object based on its state value.

Than we can create handleApiResponse function that takes an APIResponse as a parameter and uses a switch case to handle different states accordingly.

function handleApiResponse(response: APIResponse): void {
  switch (response.state) {
    case "loading":
      console.log("Loading...");
      break;
    case "success":
      console.log("Success! Data:", response.data);
      break;
    case "error":
      console.error("Error:", response.errorMessage);
      break;
    default:
      // Handle unexpected state
      console.error("Unexpected state:", response);
  }
}

Conclusion

By mastering conditional TypeScript types, you can significantly enhance your code’s type safety and expressiveness, making it a valuable skill in your TypeScript toolkit.