TS-基于值的类型获取对象类型的属性
前言
这回反过来操作,指定对象中属性的值类型,将值类型符合要求的属性更改为可选。Ts
中没有直接提供获取指定值类型的属性的方法,怎么去获取值为指定类型的属性,这是本文记叙的内容。
测试用的对象类型如下,获取其中值为 object
类型的属性名称:
type Human = {
name: string
age: number
healthy: {
illness: string
status: string
}
occupation: {
name: string
workingHours: number
}
}
代码
首先,肯定是通过值的类型去判断,有哪些属性值的类型是符合需求的,所以必定有 value extends valueType
的操作,用于判断值的类型是否符合要求。
而属性名称,说白了就是 string
的字面量类型而已,也是一个类型, 所以当条件符合时,可以直接将 key
作为属性的值类型赋予,而不符合时,将类型更改为 never
。
那么,可以写出如下代码:
// 接受两个泛型,T 为对象类型,valueType 为指定的对象类型
type PickByValueType<T extends object, valueType> = {
// 判断值 T[key] 是否符合指定的类型
// 如果符合,将该属性的值设置为 该属性名称的字面量类型,也就是 key
// 否则,将属性的值设置为 never
[key in keyof T]: T[key] extends valueType ? key : never
}
// 传入 Human 和 object
type PickByValueTypeHuman = PickByValueType<Human, object>
可以看到,除了 occupation
和 healthy
之外,其他属性值的类型都变成了 never
,而 occupation
和 healthy
的值类型变为了属性的名称。
接下来,可以通过取出所有属性的值类型,组成一个联合类型,在联合类型中,never
会被忽略掉,就只剩下 'occupation' | 'healthy'
,就是属性值为 object
的属性名称,这也是上面将不符合条件的属性值类型更改为 never
的原因。那要怎么把一个对象类型变成一个由值的类型组成的联合类型呢?
上面的 PickByValueTypeHuman
其实就是就是一个映射类型,则可以利用 PickByValueTypeHuman[key]
的方式取出其中某一个属性的值的类型:
// 取出其中 occupation 的值类型
type PickByValueTypeHuman = PickByValueType<Human, object>['occupation']
// 取出其中 name 的值类型
type PickByValueTypeHuman = PickByValueType<Human, object>['name']
可以看到通过,能够取出正确的值的类型,而访问的key
可以是一个联合类型,TS
会逐个遍历联合类型的中每一个属性名称,然后从映射类型中取出对应的值的类型,返回值的类型组成的联合类型:
type PickByValueTypeHuman = PickByValueType<Human, object>['occupation' | 'healthy' | 'name']
PickByValueTypeHuman
变成了一个由值的类型组成的联合类型,而属性 name
的值类型为 never
,在联合类型中会被忽略掉。
因此,可以直接访问全部的对象属性,就能得到所有值类型为指定类型的属性名称组成的联合类型,取值类型的操作,可以在 PickByValueType
完成,完整代码如下:
type PickByValueType<T extends object, valueType> = {
[key in keyof T]: T[key] extends valueType ? key : never
}[keyof T] // 此处使用 keyof T 表示访问全部的对象属性
type PickByValueTypeHuman = PickByValueType<Human, object>
至此,就是值的类型获取对象类型的属性的基本思路和原理了。
转载自:https://juejin.cn/post/7133060696744394788