What are Pick and Omit Utility Types in TypeScript?
Learn how to use TypeScript's Pick and Omit utility types to simplify your code and enhance type safety.
Kaushal Joshi
Sep 25, 2024 • 8 min read
TypeScript has skyrocketed in popularity among web developers for its ability to enhance JavaScript with static typing, making large-scale projects more predictable and easier to maintain. The reason it's so popular is that TypeScript helps you catch errors early and provides a toolkit of advanced features to simplify your code.
One such advanced feature is utility types. TypeScript's utility types allow developers to manipulate existing types effortlessly. Two of the most frequently used utility types are Pick
and Omit
.
In this blog, let’s understand the Pick
and Omit
utility types in TypeScript — what are they, when to use them, and how to use them.
What Are Utility Types in TypeScript?
Utility types in TypeScript are predefined helper types that enable you to transform and manipulate existing types in a concise and readable manner. They help reduce boilerplate code and allow for cleaner, more reusable type definitions. If you're working with complex interfaces or large data models, utility types like Pick
and Omit
can make your life significantly easier.
Let's explore these two utility types in detail:
The Pick
Utility Type in TypeScript
The Pick utility type is used to create a new type by selecting a subset of properties from an existing type. This can be incredibly useful when working with large interfaces, especially when you only need a few specific properties out of many. The syntax is simple:
Pick<Type, Keys>;
Type
: The original type from which properties are being selected.Keys
: The properties you want to include in your new type, either as a single key or a union of keys.
Example: Extracting specific fields
Suppose you’re working with a User
interface that has several properties, but you only need the name
and role
fields for a certain part of your application. Here’s how you can use Pick:
interface User {
id: string;
name: string;
email: string;
role: 'user' | 'editor' | 'admin';
createdAt: Date;
updatedAt: Date;
}
type UserRole = Pick<User, 'name' | 'role'>;
The newly created UserRole type is a transformation of the User type. Instead of containing all the properties of User
, it will only have the name
and role
fields, looking like this:
{
name: string;
role: 'user' | 'editor' | 'admin';
}
By using Pick
, you ensure that your new type stays in sync with the original User interface. If role
ever changes, your UserRole
type will automatically reflect that change, reducing the chances of errors.
Note: The line
role: "user" | "editor" | "admin";
defines therole
property as a union type, meaning it can only hold one of the specified string values:"user"
,"editor"
, or"admin"
.This restricts the
role
to these specific options, ensuring type safety by preventing any value outside of this set from being assigned torole
.
Real-world use case of Pick
:
Let’s say you're building a dashboard that displays users’ names and roles but not their emails, IDs, or timestamps. Instead of passing the entire User object with unnecessary fields, you can use Pick
to streamline the data passed to your component:
function renderUserRole(user: UserRole) {
console.log(`Name: ${user.name}, Role: ${user.role}`);
}
This makes your code more efficient, ensures you're only passing the data you need, and reduces the risk of handling sensitive or irrelevant information.
Practical applications of Pick
:
- Creating smaller, specialized interfaces: Extract just the fields you need for a specific function or component, helping reduce unnecessary data passing.
- Enforcing data restrictions: Use Pick to limit the properties of an object when passing it between different parts of your app, ensuring only relevant information is available.
The Omit
Utility Type in TypeScript
The Omit
type is the opposite of Pick
. While Pick
selects specific properties, Omit
excludes them. It’s useful when you have a large interface but need a version that removes certain fields. This is particularly useful when you want all the properties from a type except for a few. Here’s the syntax:
Omit<Type, Keys>;
Type
: The original type from which properties are being omitted.Keys
: The keys you want to exclude.
Example: Removing fields
Imagine you want to remove the createdAt
and updatedAt
fields from the User interface. You can do this easily with Omit
:
type UserWithoutTimestamps = Omit<User, 'createdAt' | 'updatedAt'>;
The newly created UserWithoutTimestamps
type will have all the properties from the original User
type, except for createdAt
and updatedAt
. It will look like this:
{
id: string;
name: string;
email: string;
role: 'user' | 'editor' | 'admin';
}
Here, we can ensure that properties that are related are excluded and only specified properties are selected. Let's see a real-world use case to understand it better.
Real-world use case
Let’s say you're building a form to update a user’s profile. You don’t want to deal with fields like createdAt
and updatedAt
that are automatically managed by the system. Using Omit
, you can create a cleaner, more focused type for this form, avoiding fields that shouldn't be user-editable:
function updateUserProfile(user: UserWithoutTimestamps) {
// Update user profile logic here
}
This keeps your type definitions clean and helps prevent accidental modification of system-managed fields.
Practical Applications of Omit:
- Simplifying interfaces: When you don’t need certain fields in a form or component, Omit helps streamline your types.
- Adapting types to new requirements: Easily exclude fields that are no longer relevant as your application evolves.
Combining Pick
and Omit
You can combine Pick
and Omit
to create more complex types. This can be particularly useful when you need to modify multiple aspects of a type.
Example: Streamlining complex types
Let's extend the User
interface we worked with earlier, and add some more properties:
interface UserProfile {
id: string;
name: string;
age: number;
email: string;
address: {
street: string;
city: string;
pincode: number;
country: string;
};
settings: {
theme: 'light' | 'dark';
notifications: boolean;
};
createdAt: Date;
updatedAt: Date;
}
Now, let’s say you want a type that includes only the name and email, but also excludes country from the address and notifications from settings. You can combine Pick and Omit like this:
type SimplifiedProfile = Pick<UserProfile, 'name' | 'email'> & {
address: Omit<UserProfile['address'], 'country'>;
settings: Omit<UserProfile['settings'], 'notifications'>;
};
The SimplifiedProfile
type will look like this:
{
name: string;
email: string;
address: {
street: string;
city: string;
}
settings: {
theme: 'light' | 'dark';
}
}
Let's understand what happens:
- Firstly, it picks two properties from the root level of
UserProfile
:name
andemail
. - Then, it omits
country
from a nested propertyaddress
. - And finally, it omits
notifications
from the other nested propertysettings
, only allowingtheme
to be selected.
Use cases for combining Pick
and Omit
Combining Pick
and Omit
can be highly effective when you need to tailor existing types for more specific use cases. Here are a few scenarios where this combination shines:
- Data sanitization for external APIs: When sending data to third-party services, you often want to send only the necessary fields while ensuring sensitive or irrelevant data is excluded. You can
Pick
the essential fields andOmit
the ones you don’t need. - Nested structure simplification: In complex data structures, you might want to include certain top-level fields but omit irrelevant nested properties. For instance, when dealing with deeply nested objects, combining
Pick
to extract the outer fields andOmit
to remove unnecessary nested fields ensures you're working with exactly the data you need. (Just like the example we saw above). - Custom views or UI components: Imagine a UI component that displays user profiles but doesn’t need administrative or system-level data like timestamps or internal flags. You can
Pick
user-related fields andOmit
system-specific ones to create a clean, focused type for your component.
By combining these utility types, you can precisely define the structure of your types to match the requirements of different parts of your application, making your code both flexible and efficient.
Wrapping Up
TypeScript provides a bunch of utility types that extend the functionality of types and allow you to modify them. We saw two of the most commonly used TypeScript utility types, Pick and Omit. Pick
creates a new type by selecting a specific key or a union of keys whereas Omit
does exactly the opposite. It creates a new type by excluding certain keys from an existing type.
You can learn more about Utility types in TypeScript in the official documentation.
I hope this guide helps you get more comfortable with TypeScript’s utility types! If you found it useful, feel free to share it with others in the community. I’d also love to know if it helped you. I am most active on Peerlist and Twitter. Feel free to ping me.
And hey, if you're on the lookout for a Frontend Developer Job, check out Peerlist Jobs for some great opportunities.
Until next time, happy typesafing! 👨💻