[英]typescript intersection types of function
I am using typescript and faced some problems.我正在使用 typescript 并遇到了一些问题。 The simplest demo would be:最简单的演示是:
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
I don't understand why type h
is not never
and param x
in type i
is 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?
Also, didn't understand why type e
is 2
not never
?另外,不明白为什么 type e
是2
而不是never
?
Note: non-primitive types are conventionally written in UpperCamelCase;注意:非原始类型通常用 UpperCamelCase 编写; this distinguishes them from primitive types and variable/property names.这将它们与原始类型和变量/属性名称区分开来。 I will use this convention in what follows.我将在下面使用这个约定。
There's a bunch of pieces to this question, and so there will be a bunch of pieces to its answer.这个问题有很多部分,所以它的答案也会有很多部分。
Let's tackle the easy ones first:让我们先解决简单的问题:
type G = 1 & 2 // never
The reduction to never
of empty intersections like 1 & 2
was implemented in microsoft/TypeScript#31838 and released with TypeScript 3.6.在microsoft/TypeScript#31838中实现了像1 & 2
这样的空交叉点的减少,并随never
3.6 一起发布。 Before this, 1 & 2
was treated very much like never
, in that you'd never be able to find a value that satisfies it.在此之前, 1 & 2
的处理方式与never
非常相似,因为您永远无法找到满足它的值。 There's really no conceptual difference between 1 & 2
and never
, although it's possible compiler implementation details will cause one to be treated differently from the other. 1 & 2
和never
之间实际上没有概念上的区别,尽管编译器实现细节可能会导致一个与另一个被区别对待。
Next one:下一个:
type I = ((x: 1 & 2) => 0) // why x not never
The x
is never
, but the reduction is deferred until you actually use it: x
是never
,但减少会延迟到您实际使用它:
type IParam = Parameters<I>[0]; // never
This deferral was implemented in microsoft/TypeScript#36696 and released with TypeScript 3.9.此延迟在microsoft/TypeScript#36696中实现,并与 TypeScript 3.9 一起发布。 Before this, x
was eagerly reduced to never
just like G
above.在此之前, x
被急切地简化为never
像上面的G
那样。
Now for some more complicated examples:现在来看一些更复杂的例子:
type H = ((x: 1) => 0) & ((x: 2) => 0) // why H not never
There are actually quite a few reasons why H
is not never
:实际上有很多原因H
不是never
:
The TypeScript-specific reason is because an intersection of function types is considered the same as an overloaded function with multiple call signatures:特定于 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
Notice how h
is displayed as a function with two overloads;注意h
如何显示为具有两个重载的 function; one which accepts a 1
parameter, and another which accepts a 2
parameter.一个接受1
参数,另一个接受2
参数。 It does not accept 3
, and it also does not accept 1 | 2
它不接受3
,也不接受1 | 2
1 | 2
even though from a pure type system perspective it probably should (see microsoft/TypeScript#14107 for a longstanding feature request to support this). 1 | 2
即使从纯类型系统的角度来看它可能应该(参见microsoft/TypeScript#14107以获得支持这一点的长期功能请求)。
Even if TypeScript didn't interpret function-intersections as overloads, H
should not be never
.即使 TypeScript 没有将函数交集解释为重载, H
也不应该是never
。 Function types are contravariant in their parameter types (see my answer to this question for an explanation. You can also read about the contravariance of function parameters in the TS handbook description of the --strictFunctionTypes
compiler flag ). Function 类型的参数类型是逆变的(有关解释,请参阅我对此问题的回答。您还可以在--strictFunctionTypes
编译器标志的 TS 手册描述中阅读 function 参数的逆变性)。 Contravariance turns things into their dual ;逆变将事物变成它们的对偶; If F<T>
is contravariant in T
, then F<T | U>
如果F<T>
在T
中是逆变的,则F<T | U>
F<T | U>
is equivalent to F<T> & T<U>
, and F<T & U>
is equivalent to F<T> | F<U>
F<T | U>
等价于F<T> & T<U>
,而F<T & U>
等价于F<T> | F<U>
F<T> | F<U>
. F<T> | F<U>
。 So a consistent type system, when asked for the parameter type of an intersection of functions would return the union of the function parameter types.因此,一个一致的类型系统,当要求函数交集的参数类型时,将返回function参数类型的并集。 And 1 | 2
和1 | 2
1 | 2
is not never
. 1 | 2
不是never
。
Even if H
were a completely uninhabited type, currently TypeScript only reduces intersections to never
in particular situations, and an intersection of functions is not one of those.即使H
是一个完全无人居住的类型,目前 TypeScript 只是在特定情况下将交叉点减少到never
,功能交叉点不是其中之一。 If you look in the compiler source code file checker.ts
it says "an intersection type is considered empty if it contains the type never
, or more than one unit type or an object type and a nullable type ( null
or undefined
), or a string
-like type and a type known to be non- string
-like, or a number
-like type and a type known to be non- number
-like, or a symbol
-like type and a type known to be non- symbol
-like, or a void
-like type and a type known to be non- void
-like, or a non-primitive type and a type known to be primitive."如果您查看编译器源代码文件checker.ts
,它会显示“如果交叉类型包含类型never
或多个单元类型或 object 类型和可空类型( null
或undefined
),则认为它为空,或者类string
类型和已知非string
类型,或类number
类型和已知非类number
类型,或类symbol
类型和已知非symbol
类型 - void
void
None of that says "two incompatible function types", so it is not reduced to never
.没有一个说“两个不兼容的 function 类型”,所以它没有减少到never
。
Last one:最后一个:
type E = (((x: 1) => 0) & ((x: 2) => 0)) extends (x: infer L) => 0 ? L : never;
// why E is 2 not never or 1?
Recall from earlier that an intersection of functions is considered to be an overloaded function.回想一下,函数的交集被认为是重载的 function。 It is a known design limitation of TypeScript that when using type inference on an overloaded function type, the compiler does not try to resolve the overload by figuring out which call signature matches the intended inference best. TypeScript 的一个已知设计限制是,在对重载的 function 类型使用类型推断时,编译器不会尝试通过确定哪个调用签名与预期的推断最匹配来解决过载问题。 It just uses the last call signature and ignores all the rest of them .它只使用最后一个调用签名并忽略其中的所有 rest 。 In E
, that means the compiler only sees (x: 2) => 0
when matching against (x: infer L) => 0
, and so L
is inferred as 2
.在E
中,这意味着编译器仅在与(x: infer L) => 0
匹配时看到(x: 2) => 0
> 0 ,因此L
被推断为2
。 The "right" thing to do would probably be the union 1 | 2
“正确”的做法可能是联合1 | 2
1 | 2
, but this does not happen. 1 | 2
,但这不会发生。 See microsoft/TypeScript#27027 for one instance of this limitation.有关此限制的一个实例,请参阅microsoft/TypeScript#27027 。
in case如果
type h = ((x: 1) => 0) & ((x: 2) => 0)
you assign 1
as a type for x
also known as literal type.您将1
分配为x
的类型,也称为文字类型。 Literal type is next to never
smallest set of values.文字类型几乎never
最小的值集。 The difference between never
and literal type 1
is that never
is an empty set but 1
set which has exactly one value in it. never
和字面量类型1
之间的区别在于, never
是一个空集,但1
集合中只有一个值。 So that never
!== 1
所以never
!== 1
here这里
type i = (x: 1 & 2) => 0;
type of parameter x
is never
参数x
的类型never
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.