Skip to main content

Exclude<UnionType, ExcludedMembers> Utility Types in TypeScript With Examples

What Is Exclude<UnionType, ExcludedMembers>

In this article, we will introduce and explore one such utility type: Exclude< UnionType, ExcludedMembers>. This utility type is particularly useful when working with complex type structures, as it enables developers to create a new type by excluding specific members from an existing union type.

Syntax

Here's the basic syntax for using Exclude<UnionType, ExcludedMembers>

type NewType = Exclude<UnionType, ExcludedMembers>;

For example, let's say you have a union type Animal and you want to create a new type that excludes the 'Fish' member:

type Animal = "Dog" | "Cat" | "Fish";
type LandAnimal = Exclude<Animal, "Fish">;

In this case, the LandAnimal type would be equivalent to the union type 'Dog' | 'Cat', as the Fish member has been excluded.

TypeScript Course Instructor Image
TypeScript Course Instructor Image

Time To Transition From JavaScript To TypeScript

Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.

SEE COURSE DETAILS

Example Use Case

In a real-world application, let's say we are building an e-commerce platform that sells various types of products. We have a union type Product representing all possible product types:

type Product = "Electronics" | "Clothing" | "Toys" | "Books" | "Furniture";

Now, we want to create a promotional campaign targeting only a subset of these product types, excluding Electronics and Furniture. Using the Exclude<UnionType, ExcludedMembers> utility type, we can create a new type for the targeted product categories:

type TargetedProducts = Exclude<Product, "Electronics" | "Furniture">;

The TargetedProducts type will now represent the union type 'Clothing' | 'Toys' | 'Books'.

We can then define a function to apply a discount to the targeted product categories:

interface ProductItem {
type: Product;
price: number;
}

function isTargetedProduct(
productType: Product
): productType is TargetedProducts {
const targetedProducts: TargetedProducts[] = ["Clothing", "Toys", "Books"];
return targetedProducts.includes(productType as TargetedProducts);
}

function applyPromotion(product: ProductItem): ProductItem {
const discount = 0.1;
if (isTargetedProduct(product.type)) {
product.price -= product.price * discount;
}
return product;
}

In this example, we used the Exclude<UnionType, ExcludedMembers> utility type to create the TargetedProducts type, which represents the product categories eligible for the promotional campaign. The applyPromotion function applies a discount to the product price if the product belongs to the targeted categories, ensuring type safety through the use of the isTargetedProduct type guard function.

Combining With Other Utility Types

The power of utility types lies in their ability to be combined with other utility types to create more complex types. In this example, we'll look at the use of Exclude<UnionType, ExcludedMembers> in conjunction with the Partial<T> utility type. Let's say we have an online project management application, and we want to create a feature for updating a task. We have a Task interface, which represents the task object:

interface Task {
id: number;
title: string;
description: string;
status: "ToDo" | "InProgress" | "Completed";
}

Now, we want to create an UpdateTask type to represent the task properties that can be updated, excluding 'id' as it should remain constant. We can use the Exclude utility type in combination with the keyof operator and the Partial utility type:

type UpdatableKeys = Exclude<keyof Task, "id">;
type UpdateTask = Partial<Pick<Task, UpdatableKeys>>;

The UpdatableKeys type represents all keys of the Task type excluding 'id', and the UpdateTask type represents a partial version of the Task type that includes only the updatable properties.

Now we can create a function to update a task with the new properties:

function updateTask(task: Task, updates: UpdateTask): Task {
const updatedTask: Task = { ...task, ...updates };
return updatedTask;
}

We used the Exclude<UnionType, ExcludedMembers> utility type along with the keyof operator and the Partial<T> utility type to create a type for updating tasks, ensuring that only the allowed properties can be updated while maintaining type safety.

Conclusion

Utility types in TypeScript, when examined individually, may sometimes appear to have limited use or seem redundant. It's easy to overlook their potential in isolation; however, the true power of utility types emerges when they are combined to achieve advanced levels of type checking and manipulation, resulting in more expressive and type-safe code.

By leveraging the capabilities of different utility types together, you can create complex type structures and annotations that better represent the business logic and constraints of your application. This synergy between utility types enables you to harness the full power of TypeScript's type system, providing a more robust development experience.

What Can You Do Next 🙏😊

If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for software developers.

YouTube @cloudaffle