Open semlinker opened 3 years ago
/**
* 将联合类型转为对应的交叉函数类型
* @template U 联合类型
*/
type UnionToInterFunction<U> = (U extends any ? (k: () => U) => void : never) extends (
k: infer I,
) => void
? I
: never;
/**
* 获取联合类型中的最后一个类型
* @template U 联合类型
*/
type GetUnionLast<U> = UnionToInterFunction<U> extends { (): infer A } ? A : never;
/**
* 在元组类型中前置插入一个新的类型(元素);
* @template Tuple 元组类型
* @template E 新的类型
*/
type Prepend<Tuple extends any[], E> = [E, ...Tuple];
/**
* 联合类型转元组类型;
* @template Union 联合类型
* @template T 初始元组类型
* @template Last 传入联合类型中的最后一个类型(元素),自动生成,内部使用
*/
type UnionToTuple<Union, T extends any[] = [], Last = GetUnionLast<Union>> = {
0: T;
1: UnionToTuple<Exclude<Union, Last>, Prepend<T, Last>>;
}[[Union] extends [never] ? 0 : 1];
type TupleToIntersection<T extends Array<any>> = T extends [infer F, ...infer U]
? U extends []
? F
: F & TupleToIntersection<U>
: never;
type UnionToIntersection<U> = TupleToIntersection<UnionToTuple<U>>;
// 测试用例
type U0 = UnionToIntersection<string | number>; // never
type U1 = UnionToIntersection<{ name: string } | { age: number }>; // { name: string; } & { age: number; }
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never
// 测试用例
type U0 = UnionToIntersection<string | number> // never
type U1 = UnionToIntersection<{ name: string } | { age: number }> // { name: string; } & { age: number; }
// 实现一个 UnionToIntersection 工具类型,用于把联合类型转换为交叉类型。具体的使用示例如下所示:
type UnionToIntersection<U> = (U extends U ? (arg: U) => any: never) extends (arg: infer T) => any
? T
: never;
// 测试用例
type U0 = UnionToIntersection<string | number> // never
type U1 = UnionToIntersection<{ name: string } | { age: number }> // { name: string; } & { age: number; }
利用联合类型在 extends 的时自动分发,在利用函数参数类型逆变,从而实现了联合类型到交叉类型的转变。
看了代码还是有点不懂哈 [流泪] 可以麻烦大佬再说细一点嘛...
/** * 将联合类型转为对应的交叉函数类型 * @template U 联合类型 */ type UnionToInterFunction<U> = (U extends any ? (k: () => U) => void : never) extends ( k: infer I, ) => void ? I : never; /** * 获取联合类型中的最后一个类型 * @template U 联合类型 */ type GetUnionLast<U> = UnionToInterFunction<U> extends { (): infer A } ? A : never; /** * 在元组类型中前置插入一个新的类型(元素); * @template Tuple 元组类型 * @template E 新的类型 */ type Prepend<Tuple extends any[], E> = [E, ...Tuple]; /** * 联合类型转元组类型; * @template Union 联合类型 * @template T 初始元组类型 * @template Last 传入联合类型中的最后一个类型(元素),自动生成,内部使用 */ type UnionToTuple<Union, T extends any[] = [], Last = GetUnionLast<Union>> = { 0: T; 1: UnionToTuple<Exclude<Union, Last>, Prepend<T, Last>>; }[[Union] extends [never] ? 0 : 1]; type TupleToIntersection<T extends Array<any>> = T extends [infer F, ...infer U] ? U extends [] ? F : F & TupleToIntersection<U> : never; type UnionToIntersection<U> = TupleToIntersection<UnionToTuple<U>>; // 测试用例 type U0 = UnionToIntersection<string | number>; // never type U1 = UnionToIntersection<{ name: string } | { age: number }>; // { name: string; } & { age: number; }
好像有问题 ts @zhaoxiongfei 与类型“UnionToTuple<U, [], GetUnionLast>”和“any[]”相比,堆栈深度过高。ts(2321)
看了代码还是有点不懂哈 [流泪] 可以麻烦大佬再说细一点嘛...
尝试解读3楼大佬的写法:
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never
如果U是never类型, 直接返回never类型;
否则声明一个以U
为入参类型的函数类型A, 即(k: U) => void
, 此函数继承自以I
类型为入参的函数类型B, 即(k: infer I) => void
.
如果A能继承B, 那么入参类型I一定包含所有U的类型, 所以返回的I就是所有U的交叉类型;
否则返回never @TheMaximumPotential
(k: U) => void : never) extends (k: infer I) => void ? I
求大佬讲解一下思路 文档看了 但是并没有找到什么灵感
export default {}
// 实现一个 UnionToIntersection 工具类型,用于把联合类型转换为交叉类型。具体的使用示例如下所示 type UnionToIntersection = (U extends any ? (k: U) => any : never) extends (k: infer U) => any ? U : never
// 测试用例 type U0 = UnionToIntersection<string | number> // never type U1 = UnionToIntersection<{ name: string } | { age: number }> // { name: string; } & { age: number; }
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never
将这个方法拆成两部分来看,(U extends any ? (k: U) => void : never)
是第一部分,extends (k: infer I) => void ? I : never
是第二部分
U=string|number
举例,第一部分实际上做的的操作是(string extends any ? (k:string) => void :never) | (number extends any ? (k:number) => void :never)
,简化之后的结果就是((k:string)=>void)|((k:number)=>void)
,这是两个函数类型的联合类型((k:string)=>void)|((k:number)=>void)
赋值,那么这个函数一定是(k:string&number)=>void
,从而推得函数参数类型是string&number
作为结果Likewise, multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred:
type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
? U
: never;
type T20 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string
type T21 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // string & number
// 补充说明 协助理解
// T extends U ? X : Y T 的类型为 A|B|C 会被解析为 (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
// 条件类型定义: T extends U ? X : Y; 可以简单理解为三目运算
// 分布式条件类型定义: 被检测类型是一个联合类型的时候,该条件类型被称之为分布式条件类型. 即 T 的类型为 A|B|C
//
// 在协变的位置上,同一类型变量的多个候选类型会被推断为联合类型
type Foo1
第二部分 属于 显示 传 类型 不会 触发联合类型 条件判断时 分布式计算 只会采用 非分步式计算 就是楼主说的推断情况
type UnionToIntersection<U> =
(
U extends any
? (k: U) => void
: never
) extends (k: infer P) => void ? P : never
实现一个
UnionToIntersection
工具类型,用于把联合类型转换为交叉类型。具体的使用示例如下所示: