联合类型转换交叉类型原理?

type a1 = { a: string } | { b: string } | { c: string }

需要转换成联合类型

type a2 = { a: string;} & { b: string;} & { c: string;}

这边有一个类型可以完成转换,但我不理解内部机制,请问 UnionToIntersection 含义

type UnionToIntersection<T> = (T extends any ? (a: T) => any : never) extends (
  args: infer R
) => any
  ? R
  : never;

通过分发机制将联合类型转换成函数类型我理解 (T extends any ? (a: T) => any : never)后面通过 args: infer R 我就不理解了。

阅读 2.6k
1 个回答

第一部分:(T extends any ? (a: T) => any : never)

这里比较简单,逐步替换:

T extends any ? (a: T) => any : never

({ a: string } extends any? (a: { a: string }) => any : never)
| ({ b: string } extends any? (a: { b: string }) => any : never)
| ({ c: string } extends any? (a: { c: string }) => any : never)

((a: { a: string }) => any) | ((a: { b: string }) => any) | ((a: { c: string }) => any)

正如原题所述,这里发生了分发,分成了三个函数,而不是单独一个(a: { a: string } | { b: string } | { c: string }) => any

第二部分:extends (args: infer R) => any ? R : never

我们先代入第一部分的结果:

((a: { a: string }) => any) | ((a: { b: string }) => any) | ((a: { c: string }) => any) extends (args: infer R) => any ? R : never

可以这么理解,我们要给这里填空:

declare var funcA: (a: { a: string }) => any;
declare var funcB: (a: { b: string }) => any;
declare var funcC: (a: { c: string }) => any;

var funcAll: (args: ?????????) => any;
funcAll = funcA;
funcAll = funcB;
funcAll = funcC;

但是看起来还是有点绕,不如我们把它换成函数调用:

declare var funcA: (a: { a: string }) => any;
declare var funcB: (a: { b: string }) => any;
declare var funcC: (a: { c: string }) => any;

declare var args: ?????????
funcA(args);
funcB(args);
funcC(args);

这样看起来就正常多了,用大白话解释,我们需要一个args,用它既能调用(a: { a: string }) => any),又能调用((a: { b: string }) => any),又能调用((a: { c: string }) => any)

那么很显然这个args要满足所有要求,只能是{ a: string, b: string, c: string }

这里的术语叫逆变,参数类型是逆变的。

总之,这里用了两个比较少用的概念:1. 条件类型会分发联合类型 2. 函数参数是逆变的。分发是TS的一个设计决定,逆变也受到--strictFunctionTypes的控制,如果其他语言要实现类似功能,不一定也会写成这样。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题