[英]Extending function parameter types with intersection type in Typescript
[英]typescript intersection types of function
我正在使用 typescript 并遇到了一些问题。 最简单的演示是:
type g = 1 & 2 // never
type h = ((x: 1) => 0) & ((x: 2) => 0) // why h not never
type i = ((x: 1 & 2) => 0)// why x not never
我不明白为什么类型h
不是never
而类型i
中的 param x
不是never
type e = (((x: 1) => 0) & ((x: 2) => 0)) extends (x: infer L) => 0 ? L : never; // why e is 2 not never or 1?
另外,不明白为什么 type e
是2
而不是never
?
注意:非原始类型通常用 UpperCamelCase 编写; 这将它们与原始类型和变量/属性名称区分开来。 我将在下面使用这个约定。
这个问题有很多部分,所以它的答案也会有很多部分。
让我们先解决简单的问题:
type G = 1 & 2 // never
在microsoft/TypeScript#31838中实现了像1 & 2
这样的空交叉点的减少,并随never
3.6 一起发布。 在此之前, 1 & 2
的处理方式与never
非常相似,因为您永远无法找到满足它的值。 1 & 2
和never
之间实际上没有概念上的区别,尽管编译器实现细节可能会导致一个与另一个被区别对待。
下一个:
type I = ((x: 1 & 2) => 0) // why x not never
x
是never
,但减少会延迟到您实际使用它:
type IParam = Parameters<I>[0]; // never
此延迟在microsoft/TypeScript#36696中实现,并与 TypeScript 3.9 一起发布。 在此之前, x
被急切地简化为never
像上面的G
那样。
现在来看一些更复杂的例子:
type H = ((x: 1) => 0) & ((x: 2) => 0) // why H not never
实际上有很多原因H
不是never
:
特定于 TypeScript 的原因是因为 function 类型的交集被认为与具有多个调用签名的重载 function相同:
declare const h: H; // 1/2 h(x: 1): 0 // 2/2 h(x: 2): 0 h(1); // okay h(2); // okay h(3); // error, no overload matches this call h(Math.random() < 0.5? 1: 2); // error, no overload matches this call
注意h
如何显示为具有两个重载的 function; 一个接受1
参数,另一个接受2
参数。 它不接受3
,也不接受1 | 2
1 | 2
即使从纯类型系统的角度来看它可能应该(参见microsoft/TypeScript#14107以获得支持这一点的长期功能请求)。
即使 TypeScript 没有将函数交集解释为重载, H
也不应该是never
。 Function 类型的参数类型是逆变的(有关解释,请参阅我对此问题的回答。您还可以在--strictFunctionTypes
编译器标志的 TS 手册描述中阅读 function 参数的逆变性)。 逆变将事物变成它们的对偶; 如果F<T>
在T
中是逆变的,则F<T | U>
F<T | U>
等价于F<T> & T<U>
,而F<T & U>
等价于F<T> | F<U>
F<T> | F<U>
。 因此,一个一致的类型系统,当要求函数交集的参数类型时,将返回function参数类型的并集。 和1 | 2
1 | 2
不是never
。
即使H
是一个完全无人居住的类型,目前 TypeScript 只是在特定情况下将交叉点减少到never
,功能交叉点不是其中之一。 如果您查看编译器源代码文件checker.ts
,它会显示“如果交叉类型包含类型never
或多个单元类型或 object 类型和可空类型( null
或undefined
),则认为它为空,或者类string
类型和已知非string
类型,或类number
类型和已知非类number
类型,或类symbol
类型和已知非symbol
类型 - void
void
没有一个说“两个不兼容的 function 类型”,所以它没有减少到never
。
最后一个:
type E = (((x: 1) => 0) & ((x: 2) => 0)) extends (x: infer L) => 0 ? L : never;
// why E is 2 not never or 1?
回想一下,函数的交集被认为是重载的 function。 TypeScript 的一个已知设计限制是,在对重载的 function 类型使用类型推断时,编译器不会尝试通过确定哪个调用签名与预期的推断最匹配来解决过载问题。 它只使用最后一个调用签名并忽略其中的所有 rest 。 在E
中,这意味着编译器仅在与(x: infer L) => 0
匹配时看到(x: 2) => 0
> 0 ,因此L
被推断为2
。 “正确”的做法可能是联合1 | 2
1 | 2
,但这不会发生。 有关此限制的一个实例,请参阅microsoft/TypeScript#27027 。
如果
type h = ((x: 1) => 0) & ((x: 2) => 0)
您将1
分配为x
的类型,也称为文字类型。 文字类型几乎never
最小的值集。 never
和字面量类型1
之间的区别在于, never
是一个空集,但1
集合中只有一个值。 所以never
!== 1
这里
type i = (x: 1 & 2) => 0;
参数x
的类型never
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.