Table of Contents
- Introduction
- Overview of Utility Types in TypeScript
- Pick<T, K>
- Definition and Syntax
- Use Cases and Examples
- Omit<T, K>
- Definition and Syntax
- Use Cases and Examples
- Record<K, T>
- Definition and Syntax
- Use Cases and Examples
- Practical Examples
- Example 1: Using
Pick
to Select Specific Properties - Example 2: Using
Omit
to Exclude Specific Properties - Example 3: Using
Record
for Dynamic Key-Value Mapping
- Example 1: Using
- When and Why to Use These Utility Types
- Conclusion
Introduction
TypeScript’s utility types continue to provide powerful features that allow developers to manipulate and shape types based on specific needs. In this article, we’ll explore three more utility types: Pick, Omit, and Record. These utility types provide a way to create new types from existing ones by selecting specific properties, excluding properties, or defining object types with dynamic keys.
These types are often used in scenarios where you need to create subsets of types, exclude specific properties, or map values in an object. Understanding when and how to use these utility types can help you create more flexible, type-safe code.
Overview of Utility Types in TypeScript
In Part 1 of this series, we covered the Partial, Required, and Readonly utility types, which allow you to modify properties of a type by making them optional, required, or read-only.
In this part, we’ll look at three additional utility types:
- Pick<T, K>: Creates a new type by picking a subset of properties from another type.
- Omit<T, K>: Creates a new type by omitting specific properties from another type.
- Record<K, T>: Creates an object type with keys of type
K
and values of typeT
.
These utility types help in extracting or excluding properties dynamically, which is useful when working with objects and customizing types for specific cases.
Pick<T, K>
Definition and Syntax
The Pick<T, K>
utility type allows you to create a new type by selecting a subset of properties from an existing type. You specify which properties to include by passing a union of property names K
. This makes Pick
useful when you only need a specific set of properties from a larger type.
The syntax for Pick
is:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
In this definition:
T
is the original type.K
is a subset of the keys fromT
(i.e., a union of property names).- The result is a new type with only the selected properties.
Use Cases and Examples
- Selecting specific properties: When you need to create a new type that includes only certain properties from an existing type.
- Creating simplified types: When you only need a subset of an object’s properties for a particular use case, such as in API calls or UI components.
Example:
interface User {
id: number;
name: string;
email: string;
age: number;
}
type UserInfo = Pick<User, 'name' | 'email'>;
const userInfo: UserInfo = {
name: 'Alice',
email: '[email protected]',
};
console.log(userInfo); // { name: 'Alice', email: '[email protected]' }
In this example, the UserInfo
type only contains the name
and email
properties of the User
type. This is useful when you need to pass only certain properties, such as in a scenario where only user details like name and email are required.
Omit<T, K>
Definition and Syntax
The Omit<T, K>
utility type creates a new type by excluding specific properties from another type. You specify which properties to exclude by passing a union of property names K
. This is useful when you want to remove certain properties from an object while retaining the rest.
The syntax for Omit
is:
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
In this definition:
T
is the original type.K
is a union of property names to exclude fromT
.- The result is a new type that includes all properties of
T
except those specified inK
.
Use Cases and Examples
- Excluding specific properties: When you need to create a new type that excludes certain properties, such as when working with partial data or avoiding sensitive information.
- Modifying types dynamically: When you need to create variations of an object type with certain properties removed.
Example:
interface Product {
id: number;
name: string;
description: string;
price: number;
}
type ProductPreview = Omit<Product, 'description' | 'price'>;
const productPreview: ProductPreview = {
id: 1,
name: 'Laptop',
};
console.log(productPreview); // { id: 1, name: 'Laptop' }
In this example, the ProductPreview
type omits the description
and price
properties, making it a simplified version of the Product
type. This could be useful when you need to display a preview of a product but don’t require all the details.
Record<K, T>
Definition and Syntax
The Record<K, T>
utility type creates an object type with keys of type K
and values of type T
. It is useful when you need to create a mapping of keys to values, particularly when the keys are known beforehand or can be dynamically defined.
The syntax for Record
is:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
In this definition:
K
is the type of the keys. It must extendkeyof any
, meaning it can be a string, number, or symbol.T
is the type of the values associated with each key.
Use Cases and Examples
- Dynamic key-value pairs: When you need to define an object type with a dynamic set of keys and values.
- Mapping keys to specific types: When creating a mapping of known keys to specific values, such as representing a set of states or configurations.
Example:
type Role = 'admin' | 'user' | 'guest';
type Permissions = Record<Role, boolean>;
const rolePermissions: Permissions = {
admin: true,
user: false,
guest: false,
};
console.log(rolePermissions); // { admin: true, user: false, guest: false }
In this example, the Permissions
type is created with Record<Role, boolean>
, meaning the object has keys that are one of the Role
values (admin
, user
, guest
), and each key is associated with a boolean
value.
Practical Examples
Example 1: Using Pick
to Select Specific Properties
You can use Pick
when you need to select only a subset of properties from a larger object, such as when displaying certain fields in a UI component:
interface Employee {
id: number;
name: string;
department: string;
salary: number;
}
type EmployeeOverview = Pick<Employee, 'id' | 'name'>;
const employeeOverview: EmployeeOverview = {
id: 1,
name: 'John Doe',
};
Example 2: Using Omit
to Exclude Specific Properties
You can use Omit
when you want to exclude certain properties from an object:
interface Task {
id: number;
title: string;
description: string;
status: string;
}
type TaskSummary = Omit<Task, 'description'>;
const taskSummary: TaskSummary = {
id: 1,
title: 'Complete TypeScript tutorial',
status: 'In Progress',
};
Example 3: Using Record
for Dynamic Key-Value Mapping
You can use Record
to create a dynamic mapping of keys to values:
type CountryCode = 'US' | 'CA' | 'MX';
type CountryNames = Record<CountryCode, string>;
const countries: CountryNames = {
US: 'United States',
CA: 'Canada',
MX: 'Mexico',
};
When and Why to Use These Utility Types
- Pick: Use
Pick
when you need to select a subset of properties from an existing type. This is useful when you only need specific data from an object and want to avoid unnecessary properties. - Omit: Use
Omit
when you want to exclude specific properties from a type. This is particularly useful when working with partial data or sensitive information that you don’t want to expose. - Record: Use
Record
when you need to create an object type with a dynamic set of keys and values. This is useful when building maps, dictionaries, or dynamic objects based on known key types.
Conclusion
In this second part of our exploration into TypeScript’s utility types, we’ve learned about Pick
, Omit
, and Record
. These utility types give you the ability to manipulate types in powerful ways, enabling you to create subsets, exclude properties, and map keys to specific values dynamically. By understanding when and how to use these utility types, you can write more flexible and maintainable code that aligns with TypeScript’s static typing system.
These tools are essential for working with complex data structures, ensuring that your types are both accurate and flexible enough to meet your application’s needs.