awesome-typescript
awesome-typescript copied to clipboard
「重学TS 2.0 」TS 练习题第十八题
实现一个 UnionToIntersection
工具类型,用于把联合类型转换为交叉类型。具体的使用示例如下所示:
type UnionToIntersection<U> = // 你的实现代码
// 测试用例
type U0 = UnionToIntersection<string | number> // never
type U1 = UnionToIntersection<{ name: string } | { age: number }> // { name: string; } & { age: number; }
请在下面评论你的答案。
/**
* 将联合类型转为对应的交叉函数类型
* @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; }
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types
// 实现一个 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<U>>”和“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> = (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
是第二部分
- 先看第一部分,TS中如果泛型后跟extends且这个泛型在实际传值是联合类型,则会遍历这个联合类型,取出联合类型中的每个具体类型做实际操作,最后返回每个类型的结果的联合类型
用U=string|number
举例,第一部分实际上做的的操作是(string extends any ? (k:string) => void :never) | (number extends any ? (k:number) => void :never)
,简化之后的结果就是((k:string)=>void)|((k:number)=>void)
,这是两个函数类型的联合类型 - 再看第二部分,利用了infer的函数参数类型推断,翻译一下就是如果想要找出一个函数,使它满足一定条件后能被
((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<T> = T extends { a: infer U, b: infer U } ? U : never;
type T10 = Foo1<{ a: string, b: string }>;// string
type T11 = Foo1<{ a: string, b: number }>;//string | number
// 在逆变的位置上,同一类型多个候选类型会被推断为交叉类型 , 逆变现象只会在函数类型中的函数参数上出现。
type Bar1<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never;
type T20 = Bar1<{ a: (x: string) => void, b: (x: string) => void }>; //string
type T21 = Bar1<{ a: (x: string) => void, b: (x: number) => void }>; //string & number
// ****
第二部分 属于 显示 传 类型 不会 触发联合类型 条件判断时 分布式计算 只会采用 非分步式计算 就是楼主说的推断情况
type UnionToIntersection<U> =
(
U extends any
? (k: U) => void
: never
) extends (k: infer P) => void ? P : never