TypeScript is a powerful and widely-used programming language that provides static typing capabilities to JavaScript. Developers often leverage its basic types, interfaces, extends, enums, and more in their daily coding tasks.
However, TypeScript offers more advanced types that can be incredibly useful in certain scenarios. One such advanced type is the Pick utility type, which allows you to create new types by selecting specific properties from existing types.
In this article, we will delve into the Pick type, exploring its definition, practical applications, and when it makes sense to use it.
Understanding the Pick Type Definition
The Pick utility type was introduced in TypeScript release 2.1 and is designed to generate new types by selecting a subset of properties from an existing type T. Let’s explore it further in detail by definition.
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Breaking this down:
- T represents the type from which you want to select properties.
- K is a union of keys from type T, specifying which properties you want to pick.
The key part of this definition is [P in K]: T[P];. It essentially creates a new object type by iterating through the keys in K and extracting the corresponding properties from T. This results in a type consisting of the selected properties from T.
It’s important to note that the Pick type generates a new object type, so it cannot be used to create primitive types like strings or numbers.
How to Use the Pick Type
Now that we understand the definition, let’s see how to use the Pick type in practice. Consider an example using an interface Color:
interface Color {
hex: string;
rgb: string;
}
// Generate a new type by picking the 'rgb' property
type CustomColor = Pick<Color, 'rgb'>;
const darkBlue: CustomColor = {
rgb: '(52, 70, 235)'
// 'hex' property is not part of CustomColor
}
In this example, we create the CustomColor type by using Pick to select the ‘rgb’ property from the Color interface. As a result, CustomColor only contains the ‘rgb’ property, demonstrating how you can create specialized types from existing ones.
If you want to select multiple properties, you can use a union of string literals in the K parameter:
// Select both 'rgb' and 'hex' properties
type ColorReplica = Pick<Color, 'rgb' | 'hex'>;
Recommendation: Have an Existing Defined Object Type
When using the Pick type, it’s essential to work with existing, well-defined object types. While TypeScript allows you to use Pick on non-defined object types, it’s generally more meaningful to apply it to established types.
type UserName = Pick<{
firstName: string;
middleName: string;
lastName: string;
dob: Date;
}, 'firstName' | 'lastName'>
While this code is correct, it’s more practical to define the object type explicitly:
type UserName = {
firstName: string;
lastName: string;
}
Creating well-defined object types makes your code more readable and maintainable.
Using the Pick Type When Extending an Interface
If you’re familiar with extending interfaces using the extends keyword, you’ll find it similar when using the Pick type. Let’s say we want to extend an interface CustomColor with the ‘rgb’ property:
interface CustomColor extends Pick<CustomColor, 'rgb'> { }
This extends CustomColor with only the ‘rgb’ property. The benefit of using the extends keyword in this context is that you can add additional properties to your extended interface that are distinct from those in the original CustomColor type.
interface CustomColor extends Omit<CustomColor, 'rgb'> {
hsv: string;
}
In this example, we add the ‘hsv’ property to our extended interface. This is equivalent to directly declaring the properties:
interface CustomColor {
rgb: string;
hsv: string;
}
When to Use the Pick Utility Type
You might wonder when it makes sense to use the Pick type based on the examples provided. While it may seem unnecessary in simple cases, there are scenarios where it becomes highly valuable.
One such scenario is when working with ViewModels. In a Model-View-ViewModel (MVVM) architecture pattern, ViewModels abstract the view and expose public properties and commands. Often, not all pages or components require all the information from an entity record. For instance:
type UserAvatar = Pick<User, 'email' | 'username' | 'avatarUrl'>;
Here, we create a UserAvatar type that only includes the ’email,’ ‘username,’ and ‘avatarUrl’ properties from the User interface. This is particularly useful when displaying user data in a top or side menu bar.
Another use case is when dealing with forms. Imagine you need to update a user’s email and password:
interface UserAccountForm extends Pick<User, 'email' | 'username'> {
password: string;
}
This example generates a new interface, UserAccountForm, by picking the ’email’ and ‘username’ properties from the User type and adding a ‘password’ property.
This approach helps maintain the consistency of property types between different types, reducing the risk of type mismatches.
While you can achieve similar results without using the Pick type, it excels at preserving the property types of an existing object type. If, for some reason, the ‘username’ property changes from a string to a number, you only need to update it in one place—the original type. The Pick type will automatically apply the change to all other types derived from it.
Conclusion
In conclusion, the Pick utility type is a valuable addition to TypeScript’s toolbox. It allows developers to create new types by selecting specific properties from existing types, enhancing code readability, maintainability, and type consistency.
While it may not be needed in every situation, understanding how to use the Pick type can greatly improve your TypeScript programming skills.
If you want to explore another behavior of TypeScript, I think you should also read about partials, how we can use them, and TypeScript’s Unknown Type, along with details of their real-world scenarios.
Leave a Reply