TypeScript 工具类型:Record、Omit、Exclude
TypeScript 的工具类型是一组内置的泛型类型,能够对已有类型进行转换和组合。掌握这些工具类型,可以让你用更少的代码写出类型安全、可维护性更高的代码。本文将深入讲解三个最常用的工具类型:Record、Omit 和 Exclude。
什么是工具类型
工具类型本质上是泛型类型的特殊用法,它们接收一个或多个类型作为参数,经过处理后返回一个新的类型。你可以理解为类型层面的「函数」,输入是旧类型,输出是新类型。
TypeScript 提供了很多开箱即用的工具类型,全部定义在 tslib 或内置的类型声明文件中。学会这些工具类型后,你会发现很多重复的类型定义代码都可以被简化。
Record:快速定义对象类型结构
Record<K, V> 是最基础的工具类型之一,它用于创建一个对象类型,其中所有键 K 的类型都映射到值 V 的类型。
基本语法
type Record<K extends keyof any, V> = {
[P in K]: V;
};
K 是键的类型集合,V 是所有键对应的值的类型。
实际应用
假设你需要定义一个配置对象,键是字符串,值也是字符串:
// 传统写法
interface Config {
apiUrl: string;
timeout: string;
retryCount: string;
}
// 使用 Record
type Config = Record<'apiUrl' | 'timeout' | 'retryCount', string>;
当需要动态生成枚举类型的反向映射时,Record 特别有用:
enum Status {
PENDING = 'pending',
APPROVED = 'approved',
REJECTED = 'reject',
}
// 生成反向映射对象
const statusMap: Record<Status, Status> = {
[Status.PENDING]: Status.PENDING,
[Status.APPROVED]: Status.APPROVED,
[Status.REJECTED]: Status.REJECTED,
};
进阶用法
Record 配合 keyof 可以创建灵活的字典类型:
type UserMap = Record<string, User>;
const users: UserMap = {
'user-1': { id: 1, name: 'Alice' },
'user-2': { id: 2, name: 'Bob' },
};
Exclude:精确排除不需要的类型
Exclude<T, U> 用于从类型 T 中排除可以赋值给 U 的类型。它的核心逻辑是「保留 T 中不属于 U 的部分」。
基本语法
type Exclude<T, U> = T extends U ? never : T;
这里用到了条件类型的分布式特性:当 T 是联合类型时,Exclude 会逐个检查每个成员。
实际应用
排除特定的字符串字面量:
type EventType = 'click' | 'doubleClick' | 'hover' | 'keydown';
// 排除 click,只保留其他类型
type NonClickEvent = Exclude<EventType, 'click'>;
// 结果: 'doubleClick' | 'hover' | 'keydown'
在 API 错误处理中很有用:
type ApiResponse<T> =
| { status: 200; data: T }
| { status: 400; error: string }
| { status: 500; error: string };
// 提取成功响应类型,排除错误响应
type SuccessResponse<T> = Exclude<ApiResponse<T>, { status: 400 | 500; error: string }>;
常见场景
从对象属性类型中排除 null 和 undefined:
type JsonValue = string | number | boolean | null | undefined;
// 处理前的 JSON 值可能包含 null/undefined
type NonNullableJson = Exclude<JsonValue, null | undefined>;
// 结果: string | number | boolean
Omit:删除指定的属性
Omit<T, K> 用于从对象类型 T 中删除一组属性 K,返回剩余属性组成的新类型。与 Pick 相对,Pick 是选取,Omit 是排除。
基本语法
type Omit<T, K extends keyof any> = {
[P in Exclude<keyof T, K>]: T[P];
};
Omit 内部实际上使用了 Exclude 来过滤键名,然后通过映射类型生成新的类型。
实际应用
从接口中排除敏感信息:
interface User {
id: number;
username: string;
password: string;
email: string;
createdAt: Date;
}
// 公开信息不包含 password 和 createdAt
type PublicUser = Omit<User, 'password' | 'createdAt'>;
修改第三方库的类型定义时非常实用:
import React from 'react';
// 排除原生事件中不需要的属性
type CustomInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'>;
组合多个属性
可以一次性排除多个键:
interface FormData {
name: string;
email: string;
phone: string;
address: string;
cardNumber: string;
cvv: string;
}
// 移除所有支付相关信息
type SafeFormData = Omit<FormData, 'cardNumber' | 'cvv'>;
三者组合使用
这三个工具类型可以相互配合,解决复杂的类型问题。
场景:过滤 API 响应
interface ApiResponse {
id: string;
name: string;
password: string;
token: string;
createdAt: string;
updatedAt: string;
}
// 只保留可公开展示的字段,同时确保值的类型符合预期
type PublicData = Omit<ApiResponse, 'password' | 'token'>;
type PublicDataMap = Record<keyof PublicData, string | number>;
场景:状态机类型定义
type State = 'idle' | 'loading' | 'success' | 'error' | 'cancelled';
// 排除最终状态,保留可继续流转的状态
type ActiveState = Exclude<State, 'success' | 'error' | 'cancelled'>;
// 创建状态描述映射
const stateDescriptions: Record<State, string> = {
idle: '等待操作',
loading: '加载中',
success: '操作成功',
error: '发生错误',
cancelled: '已取消',
};
性能与类型推断
使用工具类型时,TypeScript 会在编译阶段进行类型计算。对于简单的工具类型,TypeScript 的处理非常快速;但如果类型嵌套过深,可能会增加编译时间。
实际开发中建议遵循以下原则:优先使用内置工具类型而非手写复杂类型;避免在泛型参数中嵌套过多类型操作;类型别名命名要清晰,便于调试。
工具类型是 TypeScript 类型系统的精髓所在。Record 帮你快速构建对象结构,Exclude 让你精确控制类型边界,Omit 使修改接口变得简单安全。理解并熟练运用这些工具类型,能够显著提升代码的类型安全性和可维护性。

暂无评论,快来抢沙发吧!