从 A|B 得到 A&B

如何从 A|B 得到 A&B

其实 stackoverflow 上就有答案:

但是为什么呢?

我们拆开来看,这个表达式分为两部分:

第一部分是 U extends any ? (k: U) => void : never

第二部分是 extends ((k: infer I) => void) ? I : never

分开来讲,第一部分的 U extends any 是个什么鬼,为啥要判断是否 extends any?

具体可以见我这篇文章,简要的说一下就是如果 extends 左边是联合类型,那么 TS 会把 extends 操作符左边的联合类型拆开做判断。

如果第一部分的输入是 1|2 那么输出是 ((k: 1) => void) | ((k: 2) => void) 而不是 (k: 1|2) => void

这样就得到了很多个函数的联合。


然后第二部分, extends ((k: infer I) => void) ? I : never,意思是如果左边是一个函数,那就把它第一个参数的类型拿出来返回。

那么问题来了,为什么 ((k: 1) => void) | ((k: 2) => void) 的参数是 1&2?

因为函数参数是逆变的,我们假设有一个变量能同时传给 (k: 1) => void 和 (k: 2) => void,那么这个变量的类型应该是 1&2 而不是 1|2。

A extends B 意味着所有 B 都可以无条件被 A 替换。

一个函数能被 (k: 1) => void 和 (k: 2) => void 无条件替换,那么那个函数接受的参数必然既是1又是2。


但是问题又来了,根据 conditional type 的尿性,((k: 1) => void) | ((k: 2) => void) 不是应该被分开处理吗?如果分开处理那得到的结果依然会是 1|2。

苦思无果后 TS 核心贡献者 @王文璐 (也是 Vue 核心团队的)直接甩了我一脸源码

我也看不太懂,不过大概意思是 extends 左边联合类型被拆开判断的情况只会出现在左边是一个类型参数的情况。

大概就是 type F<T> = T extends xxx 的这个左边是会被拆开,而 type F = T|1 extends xxx 的左边就不会被拆开。

一脸懵逼。


完。

发布于 2019-01-06