在 TypeScript 中,有几种情况下会使用类型推断来在没有明确类型注解时提供类型信息。例如,在以下代码中
🌐 In TypeScript, there are several places where type inference is used to provide type information when there is no explicit type annotation. For example, in this code
tsTryletx = 3;
x 变量的类型被推断为 number。
这种类型的推断发生在初始化变量和成员、设置参数默认值以及确定函数返回类型时。
🌐 The type of the x variable is inferred to be number.
This kind of inference takes place when initializing variables and members, setting parameter default values, and determining function return types.
在大多数情况下,类型推断是直接明了的。 在接下来的章节中,我们将探讨类型推断的一些细微差别。
🌐 In most cases, type inference is straightforward. In the following sections, we’ll explore some of the nuances in how types are inferred.
最佳常见类型
🌐 Best common type
当从多个表达式中进行类型推断时,这些表达式的类型会被用来计算“最佳公共类型”。例如,
🌐 When a type inference is made from several expressions, the types of those expressions are used to calculate a “best common type”. For example,
tsTryletx = [0, 1, null];
要推断上面示例中 x 的类型,我们必须考虑每个数组元素的类型。这里我们有两个数组类型的选项:number 和 null。最佳公共类型算法会考虑每个候选类型,并选择与所有其他候选类型兼容的类型。
🌐 To infer the type of x in the example above, we must consider the type of each array element.
Here we are given two choices for the type of the array: number and null.
The best common type algorithm considers each candidate type, and picks the type that is compatible with all the other candidates.
因为必须从提供的候选类型中选择最佳的公共类型,所以在某些情况下,类型之间虽然有共同的结构,但没有任何类型是所有候选类型的超类型。例如:
🌐 Because the best common type has to be chosen from the provided candidate types, there are some cases where types share a common structure, but no one type is the super type of all candidate types. For example:
tsTryletzoo = [newRhino (), newElephant (), newSnake ()];
理想情况下,我们可能希望将 zoo 推断为 Animal[],但由于数组中没有严格属于 Animal 类型的对象,因此我们不会对数组元素类型进行任何推断。
要解决此问题,当没有一种类型是所有其他候选类型的超类型时,请明确提供类型:
🌐 Ideally, we may want zoo to be inferred as an Animal[], but because there is no object that is strictly of type Animal in the array, we make no inference about the array element type.
To correct this, explicitly provide the type when no one type is a super type of all other candidates:
tsTryletzoo :Animal [] = [newRhino (), newElephant (), newSnake ()];
当未找到最佳通用类型时,得到的推断类型为联合数组类型 (Rhino | Elephant | Snake)[]。
🌐 When no best common type is found, the resulting inference is the union array type, (Rhino | Elephant | Snake)[].
上下文类型
🌐 Contextual Typing
在 TypeScript 中,类型推断在某些情况下也会“反向”工作。这被称为“上下文类型”。当表达式的类型由其所在的位置来推断时,就会发生上下文类型。例如:
🌐 Type inference also works in “the other direction” in some cases in TypeScript. This is known as “contextual typing”. Contextual typing occurs when the type of an expression is implied by its location. For example:
tsTrywindow .onmousedown = function (mouseEvent ) {console .log (mouseEvent .button );Property 'kangaroo' does not exist on type 'MouseEvent'.2339Property 'kangaroo' does not exist on type 'MouseEvent'.console .log (mouseEvent .); kangaroo };
在这里,TypeScript 类型检查器使用 Window.onmousedown 函数的类型来推断赋值右侧函数表达式的类型。当它这样做时,它能够推断出 mouseEvent 参数的类型,该参数确实包含一个 button 属性,但不包含 kangaroo 属性。
🌐 Here, the TypeScript type checker used the type of the Window.onmousedown function to infer the type of the function expression on the right hand side of the assignment.
When it did so, it was able to infer the type of the mouseEvent parameter, which does contain a button property, but not a kangaroo property.
这是可行的,因为 window 的类型中已经声明了 onmousedown:
🌐 This works because window already has onmousedown declared in its type:
ts// Declares there is a global variable called 'window'declare var window: Window & typeof globalThis;// Which is declared as (simplified):interface Window extends GlobalEventHandlers {// ...}// Which defines a lot of known handler eventsinterface GlobalEventHandlers {onmousedown: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;// ...}
TypeScript 足够聪明,可以在其他上下文中推断类型:
🌐 TypeScript is smart enough to infer types in other contexts as well:
tsTrywindow .onscroll = function (uiEvent ) {Property 'button' does not exist on type 'Event'.2339Property 'button' does not exist on type 'Event'.console .log (uiEvent .); button };
根据上述函数被分配给 Window.onscroll 这一事实,TypeScript 知道 uiEvent 是一个 UIEvent,而不像前一个例子那样是 MouseEvent。UIEvent 对象不包含 button 属性,因此 TypeScript 会抛出错误。
🌐 Based on the fact that the above function is being assigned to Window.onscroll, TypeScript knows that uiEvent is a UIEvent, and not a MouseEvent like the previous example. UIEvent objects contain no button property, and so TypeScript will throw an error.
如果这个函数不处于上下文类型化的位置,函数的参数将隐式具有类型 any,并且不会发出错误(除非你正在使用 noImplicitAny 选项):
🌐 If this function were not in a contextually typed position, the function’s argument would implicitly have type any, and no error would be issued (unless you are using the noImplicitAny option):
tsTryconsthandler = function (uiEvent ) {console .log (uiEvent .button ); // <- OK};
我们还可以显式地为函数的参数提供类型信息以覆盖任何上下文类型:
🌐 We can also explicitly give type information to the function’s argument to override any contextual type:
tsTrywindow .onscroll = function (uiEvent : any) {console .log (uiEvent .button ); // <- Now, no error is given};
然而,这段代码会记录 undefined,因为 uiEvent 没有名为 button 的属性。
🌐 However, this code will log undefined, since uiEvent has no property called button.
上下文类型在很多情况下都适用。常见的情况包括函数调用的参数、赋值语句的右侧、类型断言、对象和数组字面量的成员,以及返回语句。上下文类型还可以作为最佳公共类型的候选类型。例如:
🌐 Contextual typing applies in many cases. Common cases include arguments to function calls, right hand sides of assignments, type assertions, members of object and array literals, and return statements. The contextual type also acts as a candidate type in best common type. For example:
tsTryfunctioncreateZoo ():Animal [] {return [newRhino (), newElephant (), newSnake ()];}
在这个例子中,最佳公共类型有四个候选项:Animal、Rhino、Elephant 和 Snake。在这些候选项中,Animal 可以被最佳公共类型算法选中。
🌐 In this example, best common type has a set of four candidates: Animal, Rhino, Elephant, and Snake.
Of these, Animal can be chosen by the best common type algorithm.