Utility types in TypeScript are a powerful feature that facilitates common type transformations. They provide developers with tools to manipulate types, from transforming existing ones to creating entirely new ones. These utility types are part of TypeScript's type system, designed to enhance code quality and developer productivity by enabling type checking at compile time.
The TypeScript Pick utility type is a construct that allows developers to create a new type by selecting specific properties from an existing type. It's a way to construct types with only a subset of the properties of another type, which can be helpful when you want to limit the properties that can be accessed on a particular object.
1interface Person { 2 name: string; 3 age: number; 4 email: string; 5} 6 7type PickedPerson = Pick<Person, 'name' | 'email'>; 8
In the above example, PickedPerson is a new type with only the Person interface's name and email properties.
The basic syntax of the Pick utility type is straightforward. It requires two parameters: the type you are picking from and the keys of the properties you want to pick.
1type Pick<T, K extends keyof T> = { 2 [P in K]: T[P]; 3}; 4
Here's a practical code example demonstrating how to use Pick:
1interface User { 2 id: number; 3 name: string; 4 age: number; 5 email: string; 6} 7 8// Using Pick to create a new type with only 'name' and 'age' properties 9type UserNameAndAge = Pick<User, 'name' | 'age'>; 10 11// This object is now constrained to the UserNameAndAge type 12const user: UserNameAndAge = { 13 name: 'Alice', 14 age: 30 15}; 16
In this example, UserNameAndAge is a new type that includes only the name and age properties from the User interface. The user object is then defined using this new type, ensuring it only contains the specified properties.
Creating a new type with Pick is a common task when creating a new object type that includes only a few properties from an existing type. This can help create more specific types for certain operations, reducing the possibility of passing unnecessary data around or exposing sensitive information.
Here's an example of how to create a new type using Pick:
1interface Employee { 2 name: string; 3 position: string; 4 salary: number; 5 startDate: Date; 6} 7 8// Creating a new type for public employee profiles 9type PublicEmployeeProfile = Pick<Employee, 'name' | 'position'>; 10 11// Public profile for an employee 12const publicProfile: PublicEmployeeProfile = { 13 name: 'John Doe', 14 position: 'Software Developer' 15}; 16
In this code snippet, PublicEmployeeProfile is a new type that only includes the name and position properties from the Employee interface, which might be the only information you want to display in a public setting.
When working with TypeScript, it's essential to understand the differences between the Pick and Partial utility types. Both are used to create new types, but they serve different purposes.
Pick allows you to create a new type by selecting specific properties from an existing type. On the other hand, Partial makes all properties of the given type optional, which means you can provide any subset of properties.
Here's an example to illustrate the difference:
1interface Task { 2 title: string; 3 description: string; 4 completed: boolean; 5} 6 7type PickedTask = Pick<Task, 'title' | 'completed'>; 8 9type PartialTask = Partial<Task>; 10 11const task1: PickedTask = { 12 title: 'Learn TypeScript', 13 completed: false 14}; 15 16const task2: PartialTask = { 17 title: 'Learn TypeScript' 18 // 'description' and 'completed' are optional 19}; 20
In this example, PickedTask is a new type with only the title and completed properties, while PartialTask is a type where all properties are optional.
Pick and Omit are two utility types somewhat opposite in their functionality. While Pick is used to select certain properties to create a new type, Omit is used to exclude certain properties from a type.
Here's an example showing how to use Omit:
1interface User { 2 id: number; 3 name: string; 4 age: number; 5 password: string; 6} 7 8// Using Omit to create a new type without the 'password' property 9type SafeUser = Omit<User, 'password'>; 10 11const userWithoutPassword: SafeUser = { 12 id: 1, 13 name: 'Alice', 14 age: 30 15 // 'password' is omitted from this type 16}; 17
SafeUser is a new type in this code that includes all properties from the User interface except for password. This is useful to ensure sensitive data is not included in an object.
Pick can also be used with nested objects and interfaces to select properties at a deeper level. This requires a more advanced understanding of TypeScript's type system.
Here's an example of selecting properties from nested objects using Pick:
1interface Contact { 2 phone: string; 3 address: { 4 street: string; 5 city: string; 6 zipCode: string; 7 }; 8} 9 10// Using Pick to create a type with only the 'city' property from the nested 'address' object 11type CityContact = Pick<Contact, 'phone' | 'address'> & { 12 address: Pick<Contact['address'], 'city'> 13}; 14 15const contactWithCity: CityContact = { 16 phone: '123-456-7890', 17 address: { 18 city: 'Springfield' 19 } 20}; 21
In this example, CityContact is a new type that includes the phone property from the Contact interface and only the city property from the nested address object.
Pick can be particularly powerful when used in conjunction with generics. This allows you to create reusable and dynamic types that can adapt based on the input provided.
Here's a code example showing Pick used with generics:
1function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { 2 return obj[key]; 3} 4 5const userData = { 6 name: 'John', 7 age: 25, 8 email: 'john@example.com' 9}; 10 11// Using the generic function to get the value of 'name' 12const userName = getProperty(userData, 'name'); 13
In this function, getProperty, T represents the object type, and K is the key type constrained to the keys of T. The function returns the value of the specified property key from the object.
Pick is handy when limiting the properties passed through your application. For instance, when creating view models for client-side applications, you should exclude certain properties containing sensitive data or irrelevant to the view.
Here's an example of using Pick to handle sensitive data by omitting certain keys:
1interface Account { 2 id: number; 3 username: string; 4 password: string; // Sensitive data 5 email: string; 6} 7 8// Creating a type for account details without sensitive data 9type AccountDetails = Pick<Account, 'id' | 'username' | 'email'>; 10 11const accountDetails: AccountDetails = { 12 id: 1, 13 username: 'john_doe', 14 email: 'john.doe@example.com' 15 // 'password' property is not included 16}; 17
In this example, AccountDetails is a new type that includes only the non-sensitive properties from the Account interface, ensuring the password is not exposed.
Pick can be combined with other utility types like Readonly or Record to create more complex or specific types.
Here's an example of creating a read-only pick type:
1interface Product { 2 id: number; 3 name: string; 4 price: number; 5} 6 7// Creating a read-only type for product display 8type ReadOnlyProduct = Readonly<Pick<Product, 'name' | 'price'>>; 9 10const displayProduct: ReadOnlyProduct = { 11 name: 'Gadget', 12 price: 99.99 13}; 14 15// Attempting to modify the properties will result in a TypeScript error 16// displayProduct.price = 79.99; // Error: Cannot assign to 'price' because it is a read-only property. 17
In this code, ReadOnlyProduct is a new type that includes only the name and price properties from the Product interface, and it makes them read-only, preventing any modifications.
Pick can be used with union types and function types to create new types more specific to the context in which they are used.
Here's an example of creating a new type that picks properties from a union type:
1type Shape = 2 | { kind: 'circle'; radius: number } 3 | { kind: 'square'; sideLength: number }; 4 5// Creating a type that picks the 'kind' property from the Shape union type 6type ShapeKind = Pick<Shape, 'kind'>; 7 8const circleKind: ShapeKind = { kind: 'circle' }; 9
In this example, ShapeKind is a new type that includes only the kind property from the Shape union type.
Pick contributes to type safety in TypeScript by ensuring that only the specified properties are used, which can prevent runtime errors due to missing or incorrect property types.
Here's an example showing how Pick ensures correct property types are used:
1interface Options { 2 width: number; 3 height: number; 4 color: string; 5} 6 7function createBox(options: Pick<Options, 'width' | 'height'>) { 8 // Function implementation 9} 10 11// This call is valid as only 'width' and 'height' are required 12createBox({ width: 100, height: 200 }); 13 14// This call will result in a TypeScript error as 'color' is not part of the picked properties 15// createBox({ width: 100, height: 200, color: 'blue' }); // Error: Object literal may only specify known properties. 16
In this code, the createBox function requires an object with only width and height properties, ensuring that the correct types are passed to the function.
While the Pick utility type is powerful, there are limitations and considerations to consider. One limitation is that Pick cannot be used to pick properties from types where the keys are not known ahead of time, such as index signatures.
Additionally, overusing Pick can lead to a proliferation of small types, which can clutter the codebase and make it harder to maintain. It's important to balance the use of Pick with the need for clarity and simplicity in your type definitions.
To use Pick effectively in TypeScript projects, consider the following best practices:
Here's an example illustrating best practices in action:
1interface User { 2 id: number; 3 name: string; 4 email: string; 5 role: string; 6} 7 8// Good practice: Creating a type for user display information 9type UserDisplayInfo = Pick<User, 'name' | 'email'>; 10 11// Overuse: Creating too many specific types for each component 12// type UserName = Pick<User, 'name'>; 13// type UserEmail = Pick<User, 'email'>; 14// ... and so on for each property 15
In this example, UserDisplayInfo is a well-defined type representing the necessary information for displaying a user while avoiding creating particular types for each property.
The Pick utility type is valuable in the TypeScript developer's toolkit. It allows for creating precise types by selecting specific properties from existing types, contributing to cleaner, more maintainable, and more readable code.
By understanding and applying Pick appropriately, along with other utility types, developers can ensure that their TypeScript codebases remain robust, scalable, and easy to work with. Remember to use Pick judiciously and with other TypeScript features to get the most out of this powerful language feature.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.