TypeScript Utility Types
Utility Types are a series of built-in advanced types in TypeScript.
They help developers quickly create and transform types, improving code reusability and type safety.
Utility types are essentially generic types implemented through mapped types and conditional types.
Why Are Utility Types Needed?
In actual TypeScript development, we often need to create new types based on existing types.
Manually creating these types is not only tedious but also error-prone.
Utility types provide a declarative way to transform and create types, greatly improving development efficiency.
Concept Explanation:Utility types are a series of predefined generic types in TypeScript. They use mapped types and conditional types to transform and generate types.
Partial<T> - Optional Properties
Partial sets all properties of type T to be optional.
This is very useful when creating partially updated objects or handling form data.
Example
interface User {
// User ID
id: number;
// Username
name: string;
// User email
email: string;
}
// Partial: makes all properties optional
// All properties in the transformed type are optional
type PartialUser = Partial<User>;
// Use the PartialUser type
// You can provide only some properties, not all
var user: PartialUser = { name: "Alice" };
console.log("Partial user: " + JSON.stringify(user));
Run result:
部分用户: {"name":"Alice"}
Application Scenarios:Partial is often used to update object data. For example: when updating only part of a user's information, you don't need to pass the entire user object.
Required<T> - Required Properties
Required is the opposite of Partial; it makes all optional properties required.
Use it when you need to ensure an object contains all properties.
Example
interface Config {
// Server address
host?: string;
// Port number
port?: number;
}
// Required: makes all optional properties required
// All properties in the transformed type are required
type RequiredConfig = Required<Config>;
// Use the RequiredConfig type
// Must provide all properties
var config: RequiredConfig = { host: "localhost", port: 8080 };
console.log("Config: " + JSON.stringify(config));
Run result:
配置: {"host":"localhost","port":8080}
Note:Required not only removes the optional marker (?), but also removes the readonly modifier.
Readonly<T> - Readonly Properties
Readonly sets all properties to readonly.
It is very useful when creating objects that cannot be modified after creation.
Example
interface User {
// Username
name: string;
// User age
age: number;
}
// Readonly: makes all properties readonly
// None of the properties in the transformed type can be modified
type ReadonlyUser = Readonly<User>;
// Create a readonly user object
var user: ReadonlyUser = { name: "Alice", age: 25 };
// Attempting to modify a readonly property will cause an error
// user.name = "Bob"; // Error: readonly property cannot be modified
console.log("Readonly user: " + JSON.stringify(user));
Application Scenarios:Readonly is often used to define configuration objects, enum value mappings, and other data that should not be modified.
Pick<T, K> - Selecting Properties
Pick selects the specified properties K from type T to form a new type.
Use it when you only need some properties of a type.
Example
interface User {
// User ID
id: number;
// Username
name: string;
// User email
email: string;
// User password
password: string;
}
// Pick: select the specified properties to form a new type
// Select the id and name properties from User
type UserBasicInfo = Pick<User, "id" | "name">;
// Use the selected type
var user: UserBasicInfo = { id: 1, name: "Alice" };
console.log("User basic info: " + JSON.stringify(user));
Run result:
用户基本信息: {"id":1,"name":"Alice"}
Comparison:Combining Pick and Partial can create a type that contains only some optional properties.
Omit<T, K> - Excluding Properties
Omit excludes the specified properties K from type T and returns a new type composed of the remaining properties.
It is the opposite of Pick and is used to remove unneeded properties.
Example
interface User {
// User ID
id: number;
// Username
name: string;
// User email
email: string;
// User password
password: string;
}
// Omit: exclude the specified properties
// Exclude the password property from User
type UserWithoutPassword = Omit<User, "password">;
// Use the resulting type after exclusion
var user: UserWithoutPassword = { id: 1, name: "Alice", email: "a@b.com" };
console.log("User without password: " + JSON.stringify(user));
Run result:
无密码用户: {"id":1,"name":"Alice","email":"a@b.com"}
Tip:Omit is the reverse of Pick. When you need to exclude a few properties, use Omit; when you need to select a few properties, use Pick.
Record<K, T> - Constructing Object Types
Record constructs an object type with keys of type K and values of type T.
It is often used to create key-value mappings, dictionary types, etc.
Example
type Role = "admin" | "user" | "guest";
// Record: constructs an object type
// Keys are of type Role, values are of type string array
type RolePermissions = Record<Role, string[]>;
// Use Record to create a permission mapping
var permissions: RolePermissions = {
// Admin has all permissions
admin: ["read", "write", "delete"],
// Regular user has read and write permissions
user: ["read", "write"],
// Guest only has read permission
guest: ["read"]
};
console.log("Admin permissions: " + permissions.admin);
console.log("Guest permissions: " + permissions.guest);
Run result:
管理员权限: read,write,delete 访客权限: read
Application Scenarios:Record is often used in scenarios where values need to be quickly looked up by key, such as configuration mappings, state mappings, and permission mappings.
Exclude<T, U> - Excluding Types
Exclude removes from type T all types that are assignable to type U.
Mainly used for union types, excluding specific type members.
Example
type T = "a" | "b" | "c" | "d";
// Exclude: remove specified types from T
// Exclude "a", "b", "c", leaving only "d"
type NonABC = Exclude<T, "a" | "b" | "c">;
// Using the excluded type, only "d" can be assigned
var value: NonABC = "d";
console.log("Value: " + value);
Output:
值: d
Explanation:The principle of Exclude is: if a type in T is assignable to U, remove it from T.
Extract<T, U> - Extracting Types
Extract is the opposite of Exclude; it extracts from type T the types that are assignable to type U.
Used to filter specific members from a union type.
Example
type T = "a" | "b" | "c" | 1 | 2 | 3;
// Extract: extract specified types from T
// Extract all string types: "a", "b", "c"
type Letters = Extract<T, string>;
// Use the extracted type
var letter: Letters = "a";
console.log("Letters: " + letter);
Output:
字母: a
Comparison:Extract and Exclude are complementary. Extract(T, U) is equivalent to Exclude(T, Exclude<T, U>).
NonNullable<T> - Excluding Nullish Values
NonNullable removes null and undefined from type T.
Use it to ensure a type does not contain nullish values.
Example
type T = string | null | undefined | number;
// NonNullable: remove null and undefined
// After conversion, only string and number remain
type NotNull = NonNullable<T>;
// Use the non-nullable type
var value: NotNull = "hello";
value = 42;
// Assigning null will cause an error
// value = null; // Error: cannot assign null
console.log("Value: " + value);
Explanation:NonNullable<T> is equivalent to Exclude<T, null | undefined>.
ReturnType<T> - Getting the Return Type
ReturnType gets the return type of a function type T.
Often used to extract the return type from an existing function.
Example
function getUser() {
return { name: "Alice", age: 25 };
}
// Define a function to get config
function getConfig() {
return { host: "localhost", port: 8080 };
}
// ReturnType: get the function's return type
// Extract the return type of the getUser function
type UserType = ReturnType<typeof getUser>;
// Extract the return type of the getConfig function
type ConfigType = ReturnType<typeof getConfig>;
// Use the extracted return types to create objects
var user: UserType = { name: "Bob", age: 30 };
var config: ConfigType = { host: "example.com", port: 3000 };
console.log("User: " + JSON.stringify(user));
console.log("Config: " + JSON.stringify(config));
Output:
User: {"name":"Bob","age":30}
配置: {"host":"example.com","port":3000}
Tip:T in ReturnType<T> must be a function type. You can use typeof to get the type of a function.
Notes
- Utility types are all generic:When using them, pass concrete type arguments
- Readonly and optional:Utility types can be combined, e.g., Partial<Readonly<T>>
- Built-in types:These utility types are built into TypeScript and require no installation
- Custom utility types:You can create custom utility types based on mapped types and conditional types
Advanced:If the built-in utility types do not meet your needs, you can implement custom utility types by referencing the TypeScript source code.
Summary
TypeScript utility types are an important part of the type system.
- Partial<T>:Make all properties optional
- Required<T>:Make all properties required
- Readonly<T>:Make all properties readonly
- Pick<T,K>:Select specified properties
- Omit<T,K>:Exclude specified properties
- Record<K,T>:Construct object types
- Exclude/Extract:Type filtering
- NonNullable:Exclude null and undefined
- ReturnType:Get function return type
Best practices:Making good use of utility types can make code more type-safe, reduce repetitive type definitions, and improve code maintainability.
Other extensions