Table of contents
Understanding the Challenge
Imagine you have a union of three roles: ‘user’ | ‘admin’ | ‘superuser’. You’re faced with the task of creating a type helper to remove one of these roles dynamically. Unlike arrays or objects where you can iterate over elements, unions present a unique challenge. How do you map over each member of the union?
TypeScript handles this challenge automatically through distributivity. This feature allows you to treat a union as if it were a single entity. Let’s dive into an example to illustrate how this works:
type RemoveAdmin<T> = T extends "admin" ? never : T;
In this example, we’re creating a type helper RemoveAdmin that takes a type parameter T. We check if T extends the type c. If it does, we return never, indicating that c should be removed. If not, we return T, preserving the other roles.
What’s fascinating is that TypeScript’s distributivity goes beyond expectations. When using a conditional type check like the one above, TypeScript automatically maps over each member of the union. This means we can manipulate the union in unexpected ways.
type Role = "user" | "admin" | "superuser"; type RemoveAdmin<T> = T extends "admin" ? never : T; type WithoutAdmin = RemoveAdmin<Role>; // "user" | "superuser"
This distributive behavior opens up a world of possibilities for working with unions in TypeScript. You can now create sophisticated type manipulations and transformations with ease. Whether you’re filtering out specific types or transforming the entire union, distributivity empowers you to write more expressive and concise code.
TypeScript’s distributivity is a powerful and often overlooked feature that simplifies working with unions. By understanding how it automatically maps over each member during conditional type checks, you can leverage this capability to create more dynamic and flexible type helpers. As you explore TypeScript further, keep distributivity in mind—it might just be the key to unlocking new possibilities in your type definitions.