携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
书接上回,上节我们讲了IsAny、IsEqual、IsUnion、IsNever以及IsTuple这几个判断ts特殊类型的工具类型实现,这次我们会更进一步,探讨更高级的用法
如果还没看上节的话建议先看看 -- 「TS类型体操」🔥巧用TS特殊类型特性(上)
UnionToIntercetion
这个工具类型的作用是将联合类型转成交叉类型,这是一个逆变的过程,那么ts支持逆变吗?
答案是支持的,在函数参数中,如果一个函数参数有多种类型,那么参数类型会变成这些多个类型的交叉类型,这就是一个逆变的过程
而我们就可以利用这一点,结合联合类型的分布式特性,将它们拆开成单独类型,然后作为函数参数,再用infer从函数参数中提取出结果,得到的就是对应的联合类型了
// 利用 ts 的函数参数类型的逆变特性实现
// 当函数参数有多种类型的时候,会被逆变为它们的交叉类型
// 根据联合类型的分布式特性,将类型拆散后作为函数参数类型
// 再提取函数参数类型即可得到它们的交叉类型
type UnionToIntercetion<U> = (U extends U ? (x: U) => unknown : never) extends (
x: infer R,
) => unknown
? R
: never
// type Res = {
// foo: 1;
// } & {
// bar: 2;
// }
type Res = UnionToIntercetion<{ foo: 1 } | { bar: 2 }>
GetOptional
该工具类型可以获取索引类型(可以理解成是对象)的可选属性,可选属性有什么特点呢?
可选属性最显著的特点就是它的类型是undefined与值存在时的值类型的联合类型
我们就可以利用这一点来进行提取,思路是遍历每个属性,如果{} extends 单个属性成立的话,说明这个属性可能为undefined
// 利用可选属性的类型为 undefined | xxx 的特性
// 判断 {} extends 属性是否成立,成立的话说明属性的类型中有 undefined
// 这时候得到的结果就是 {},所以可以通过这种方式去判断
type GetOptional<Obj extends Record<string, any>> = {
[P in keyof Obj as {} extends Pick<Obj, P> ? P : never]: Obj[P]
}
interface Foo {
name: string
bar: string
age?: number
baz?: number
}
// type Res = {
// age?: number | undefined
// baz?: number | undefined
// }
type Res = GetOptional<Foo>
GetRequired
趁热打铁,既然可以提取可选属性,那么也可以提取非可选属性,也就是必要属性,刚好和可选属性是对立的,所以我们只需要在对属性的判断逻辑处取反即可
type GetRequired<Obj extends Record<string, any>> = {
[P in keyof Obj as {} extends Pick<Obj, P> ? never : P]: Obj[P]
}
interface Foo {
name: string
bar: string
age?: number
baz?: number
}
// type Res = {
// name: string
// bar: string
// }
type Res = GetRequired<Foo>
RemoveIndexSignature
现在有下面这样一个索引类型
type Foo = {
[prop: string]: any
say(): void
}
这里的[prop: string]就是索引签名,意思是有任意多个字符串索引,而下面的say是具名索引,有自己的名字,说明其为string类型,而索引签名不具有名字,即不可能是string类型
现在我希望实现一个工具类型,能够将索引类型中的索引签名删除掉,也就是我希望得到:
type Foo = {
say(): void
}
刚刚已经说了,索引类型没有名字,也就是索引不可能是string,可以利用这一点来实现
type Foo = {
[prop: string]: any
say(): void
}
type RemoveIndexSignature<T extends Record<string, any>> = {
[P in keyof T as P extends `${infer Str}` ? Str : never]: T[P]
}
type Res = RemoveIndexSignature<Foo>
ClassPublicProps
ts的类中有public、private和protected属性,现在我希望提取出一个类的所有public属性组成一个新的索引类型
首先要知道一点,keyof提取类的属性时,只能提取出public的属性
class Foo {
public name: string
private age: number
protected sex: string
constructor() {
this.name = 'Plasticine'
this.age = 21
this.sex = 'male'
}
}
// keyof 只能提取出 public 属性
// 利用这一点来实现提取 public 属性组成新的索引类型
type FooKeys = keyof Foo // "name"
那就很简单了,直接利用keyof遍历所有public属性即可
type ClassPublicProps<T> = {
[P in keyof T]: T[P]
}
// type Res = {
// name: string
// }
type Res = ClassPublicProps<Foo>