最近在搬砖的时候,遇到一个场景,需要根据已存在的联合类型,将其转为交叉类型:
type Preson = {
name: string
} | {
age: number
} | {
needMoney: boolean
}
type Result = Uinon2Intersection<Preson>
期望通过 Uinon2Intersection
转换后,得到的 Result
:
type Result = {
name: string
} & {
age: number
} & {
needMoney: boolean
}
刚开始感觉很简单。我想已经会了类型体操基本动作四件套了。通过遍历联合类型,然后遍历的时候通过 key
读取属性值就行了,我啪啪啪就写出来了,就像这样:
type U2I<T> = {
[key in keyof T]: T[key]
}
type Result = U2I<Preson>
实际得到的是:
type Result = U2I<{
name: string;
}> | U2I<{
age: number;
}> | U2I<{
needMoney: boolean;
}>
Nmmm,这完全不是我期望的样子啊,然后又想了想基础四件套,感觉遇到坑了,好像仅靠四件套并不能解决啊。
先说下,上面这种情况是因为**对于联合类型,在遍历操作或者进行条件类型判断的时候,会发生类型分配**。就像下面:
type ToArray<Type> = Type extends any ? Type[] : never;
type StrArrOrNumArr = ToArray<string | number>;
其实得到:
type StrArrOrNumArr = string[] | number[]
如果想得到: (string | number)[]
。你需要这么写:
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;
回到正文,如果说我们想通过一个工具类型实现联合类型到交叉类型的转换,那需要了解一下下面几个 ts
关键概念:协变、逆变、双向协变、不变性。
类型兼容与可替代性
我们先说说类型兼容与可替代性,因为这两个概念与协变、逆变密切相关。
Typescript
的类型兼容特性是基于类型结构的,其基本规则是:如果 y 类型至少有一些与 x 类型相同的成员,则 x 类型与 y 类型兼容。
例如,设想一个名为 Pet
的接口的代码,该接口有一个 name
属性;一个名为 Dog
的接口,该接口有 name