Menu

TypeScript Pick:从类型中选取属性(附示例)

Pick<T, K> 会创建一个只包含 T 中键在 K 里的属性的类型。本文讲解如何用 Pick 选取一个或多个键、它如何检查键、Pick 与 Omit 的区别、从嵌套类型中选取、带类型的 pick() 函数,以及按值类型选取属性。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

Pick<T, K> 是一个内置工具类型,它创建的对象类型只包含 T 中 K 所列出的属性。当一个函数或组件只需要某个大类型中的几个字段时,就可以用它:

renderPreview 既接受完整的 User,也接受普通的 { id, name } 对象,因为它只要求自己用到的字段。这样它更容易调用,也更容易测试。Pick 只作用于类型:运行时,完整的 ada 对象仍然拥有全部五个属性。

语法与键检查

Pick<Type, Keys>

Keys 可以是单个键,也可以是多个键组成的联合类型,并且每个键都必须在 Type 上存在。TypeScript 标准库中的定义把这一点写成了约束:

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

由于有 K extends keyof T,拼错或不存在的键会导致编译错误,编辑器也能自动补全键名:

错误信息是 error TS2344: Type '"cost"' does not satisfy the constraint 'keyof Product'.。这一点和 Omit 不同,Omit 的键不会被检查。

Pick 保留 readonly 和可选修饰符

Pick 是对 T 的键做映射,所以每个被选取的属性都会保留自己的修饰符:

最后一行输出 { id: 8 },因为 readonly 只是编译时检查:这次赋值被报告了,又被 @ts-expect-error 压制,最后照样执行了。如果想在选取的同时改变修饰符,就把结果包一层:Partial<Pick<Account, "plan">> 或 Required<Pick<Account, "email">>。

Pick 与 Omit 对比

Pick 指定要保留什么,Omit 指定要去掉什么。今天两者可能给出相同的结果,但当源类型增加属性时,它们的表现就不一样了:

Pick<T, K>Omit<T, K>
你列出的是要保留的键要移除的键
T 上的新属性不会包含自动包含
K 中有未知的键编译错误(TS2344)静默接受
适用于API 响应、公开视图、props“除了机密字段之外的全部”

对于要离开服务器的数据,Pick 是更安全的默认选择:除非有人有意添加,否则什么都不会出现在类型里。

从嵌套类型中选取

Pick 只能看到顶层的键:像 Pick<Customer, "address.city"> 这样的点号路径会报错(TS2344),而不是选取嵌套属性。先用索引访问类型取到嵌套类型,再从中选取:

Customer["address"] 就是 address 属性的类型。同样的语法也可以取到数组元素:对于 orders: Order[] 属性,Customer["orders"][number] 就是 Order。

带类型的 pick() 函数

Pick 在运行时什么也不做。一个小型泛型函数可以把选中的属性复制到新对象中,并且返回值的类型恰好是 Pick<T, K>:

K extends keyof T 让 TypeScript 能从字符串参数推断出 K,所以返回类型恰好只包含被选取的键。之所以需要 {} as Pick<T, K> 断言,是因为空对象一开始并没有这些属性;循环会把它们填进去。

按值类型选取属性

Pick 按键名选取。如果要按属性的类型选取,比如“所有字符串字段”,就写一个带键过滤的映射类型:

as 子句在属性类型匹配时把键映射为它自己,否则映射为 never,而 never 键会被丢弃。有一个细节:可选属性的类型包含 undefined,所以这里可选的 subtitle?: string 不会被算作 string;要把它包含进来,就用 string | undefined 来判断。

常见问题

TypeScript 中的 Pick 有什么作用?

Pick<T, K> 会构建一个对象类型,只包含 T 中键在 K 里的属性。如果 User 除了 id 和 name 还有其他属性,Pick<User, "id" | "name"> 就是 { id: number; name: string }。它只作用于类型,运行时不会从对象中删除任何东西。

Pick 和 Omit 有什么区别?

Pick 列出要保留的属性,Omit 列出要移除的属性。当源类型新增属性时,Omit 会自动包含它,Pick 则不会。Pick 还会检查它的键在类型上是否存在;Omit 接受任何键。

TypeScript 中如何用 Pick 选取多个属性?

传入多个键组成的联合类型:Pick<User, "id" | "name" | "email">。这些键都必须在 User 上存在,否则编译器会报错 TS2344。

TypeScript 中如何用 Pick 选取嵌套属性?

Pick 只能选取顶层属性。先用索引访问类型取到嵌套类型,再从中选取:Pick<User["address"], "city">。如果要保留嵌套结构,就把两者组合起来:Pick<User, "id"> & { address: Pick<User["address"], "city"> }。

Pick 会保留 readonly 和可选修饰符吗?

会。Pick 是对 T 的键做映射的映射类型,所以在 T 中是 readonly 或可选的属性,在选取后的类型中仍然保持不变。

Coddy 编程语言插图

和 Coddy 一起学编程

开始