TypeScript | How to Use Pick? Pick Utility Type Explained

It is common for TypeScript developers to use primitive types, interfaces, extends, enums, the most common types. However, when you start talking about using more “advanced” types such as the Partial, Omit, or the Pick type, there is a chance some won’t know how to use them because they don’t even know these types existed. If you have never heard of the Pick utility type, we will teach everything about it in this article.

The pick utility type, introduced in TypeScript release 2.1, was developed to generate new types as the result of picking or selecting a set of properties from an existing type T. In other words, the new type is a subgroup of properties that are extracted from T properties.

Understanding the Pick type Definition

If you have TypeScript installed in your machine, the Pick utility type definition can be found in the file typescript/lib/lib.es5.d.ts. Also, TypeScript’s Github is publicly accessible. Hence, you can check out the definition of the Pick type in their repository.

/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

The first thing you notice is that the pick type generates a new object. Hence, it is not possible to have a primitive type as the new type, such as a string or a number.

As in other articles, we like to know everything that’s going on in the definition of this type by breaking it down into little pieces to understand each part of the “equation”. The pick type definition uses other keywords you might have seen before: extends, keyof, and in. Don’t worry if you don’t know them all, we’ll explain each.

<T, K extends keyof T>    // what is this?
[P in K]: T[P];           // what is this?
[P in K]                  // what is this?
K extends keyof T         // what is this?
K                         // what is this?
keyof T                   // what is this?
T[P]                      // what is this?

Let’s start from the bottom to the top, more specifically T[P]. T is the type where the pick type selects existing properties and P is the property. Therefore, by using T[P] we access the type of a property.

interface Color {
   hex: string;
   rgb: string;
}

// this is a way to access the type of the hex property
Color['rgb'] // string type

// which is the same as T[P] where T is Color and rgb is P

The next part of the “equation” is keyof T. The keyword keyof gets the keys of type T.

keyof Color = 'hex' | 'rgb'

Hence, K which is means Keys uses extends to get its definition. The keyword extends means to get all from its parent, wherein keyof T is the parent. Hence, K could be either the key 'hex' or 'rgb'. That’s why the IDE is able to “intelligently” show valid property options when you use the Pick along with a type T.

IDE showing property options of T

Finally, we have [P in K] which means that property P needs to be one of the keys extracted from K. Therefore, P could be either the key 'hex' or 'rgb'.

How to use the Pick type

After understanding all the different parts of the definition of Pick<T, K> type, where T is the type and K are the selected keys, simply provide T, K to generate a new type.

interface Color {
  hex: string;
  rgb: string;
}

// Generate the CustomColor type by taking as a reference
// the type Color. However, only pick the rgb property to
// to generate the CustomColor type
type CustomColor = Pick<Color, 'rgb'>;

const darkBlue: CustomColor = {
  rgb: '(52, 70, 235)'
  // hex property is not part of CustomColor
}

In case you want to select multiple K keys from T type, use a union of string literals.

type ColorReplica = Pick<Color, 'rgb' | 'hex'>;

Recommendation: Have an existing defined object type to apply the pick type

The main aspect to know before using the pick type is to have defined an existing object type and decide the property values to remove from the property type.

Note: By saying the previous statement, we are not saying you have to have an existing object type to use it with the pick type helper (Pick<T, K>). In fact, T can be a non-defined object type and the Pick type will generate a new object based on the K keys passed from the object.

type UserName = Pick<{
  firtName: string;
  middleName: string;
  lastName: string;
  dob: Date;
}, 'firstName' | 'lastName'>

While the logic in the previous example is correct, the Pick type wasn’t meant to be used in this way.

Why generate a new type based on a non-defined type when you can build the new type without using the Pick utility type?

type UserName = {
  firtName: string;
  lastName: string;
}

How to use the Pick type when extending an interface?

If you have used the keyword extends on an interface to be inherited from another interface, you will find it a similar process using the pick type.

In the previous examples, we used the pick type to generate the type CustomColor. This time, we are going to generate a CustomColor interface to demonstrate how to use the extends keyword with the Pick type to create a new interface.

interface CustomColor extends Pick<CustomColor, 'rgb'> { }

The benefit of using the extends keyword to generate a new interface is to add other properties different from the inherited properties from the custom type generated by using the Pick type such as in the following example:

interface CustomColor extends Omit<CustomColor, 'rgb'> {
   hsv: string;
}

// This is equivalent to the above
interface CustomColor {
   rgb: string;
   hsv: string;
}

When to use the Pick utility type?

Thinking about using the Pick type based on the examples shown in this article might give you the idea of being unnecessary. However, the recommendation is to use it if it makes sense.

For example, I like using the Pick and Omit type to generate ViewModels types or interfaces and preserve the property types of an existing interface. Let’s take a look a the following User interface and explain furthermore the concept of ViewModels if you haven’t heard of them before.

interface User {
  id: string;
  firtName: string;
  middleName: string;
  lastName: string;
  username: string;
  email: string;
  dob: Date;
  avatarUrl: string;
  dateCreated: Date;
  createdBy: string;
  dateUpdated: Date;
  updatedBy: string;
  isActive: boolean;
}

The concept of ViewModel comes from the Model-View-ViewModel architecture pattern and it is an abstraction of the view exposing public properties and commands. Assume a typical application has different views to display user data. However, not all the pages will use all the information of a user record.

A good example is to have the avatar of the user and the username or email next to it display in a top or side menu bar, making the pick type a good candidate to generate a new type representing this new ViewModel.

type UserAvatar = Pick<User, 'email' | 'username' | 'avatarUrl'>;

Another scenario is whenever there is a form to update the email and password. While there is no password property in the User interface, we can generate a new interface and “pick” the email property from the User type.

interface UserAcccountForm extends Pick<User, 'email' | 'username'> {
   password: string;
}  

Once again, you could start thinking: Why not make these types without using the Pick utility type?

Sure, that is another and the more straightforward option.

type UserAvatar = {
  email: string;
  username: string;
  avatarUrl: string;
}

interface UserAccountForm {
  email: string;
  password: string;
}

However, the benefit of using the Pick type is persisting the property types of an existing object type. In that way, if for whatever reason the username property is no longer a string, but it becomes a number, we wouldn’t have to worry about updating all the types with the username property to be a number.

// updating in multiple types the username type from string to a number
type UserAvatar = {
  email: string;
  username: number;  // updating here 
  avatarUrl: string;
}

interface UserAccountForm {
  email: string;
  username: number; // and updating here
  password: string;
}

Think of T type, used to generate new types using the pick utility type, to be the source of truth.

// We only had to update the username property type once, and let Pick<T, K> apply the changes to the rest of typpes
interface User {
  id: string;
  firtName: string;
  middleName: string;
  lastName: string;
  username: number; // updated from string to number
  email: string;
  dob: Date;
  avatarUrl: string;
  dateCreated: Date;
  createdBy: string;
  dateUpdated: Date;
  updatedBy: string;
  isActive: boolean;
}

// "username" property in UserAvatar is now a number
type UserAvatar = Pick<User, 'email' | 'username' | 'avatarUrl'>;

// "username" property in UserAcccountForm is now a number
interface UserAcccountForm extends Pick<User, 'email' | 'username'> {
   password: string;
}

Other TypeScript articles you might be interested in reading

There is a list of TypeScript articles you might be interested in checking out

If none of them are of your interest, feel free to check out the blog to check for more TypeScript and other web development-related articles.

Conclusion

All in all, the Pick is a great addition to the family of types available in TypeScript as it allows us to generate new types off of existing types. This becomes useful when looking to persist the same property types of the main type, which represents an entity type in the database, across different view models using the same entity type.

Did you like this article?

Share your thoughts by replying on Twitter of Become A Better Programmer or to personal my Twitter account.