Skip to content

类型推断 ​

在 TypeScript 中,有几个地方在没有显式类型注释的情况下使用类型推断来提供类型信息。例如,在这段代码中

ts
let x = 3;
   
// let x: number

变量的类型x被推断为number。这种推理发生在初始化变量和成员、设置参数默认值以及确定函数返回类型时。

在大多数情况下,类型推断很简单。在下面的部分中,我们将探讨如何推断类型的一些细微差别。

最佳通用型 ​

当从多个表达式进行类型推断时,这些表达式的类型用于计算“最佳公共类型”。例如,

ts
let x = [0, 1, null];
   
// let x: (number | null)[]

要推断上例中 的类型x,我们必须考虑每个数组元素的类型。这里我们对数组的类型有两种选择:number和null。最佳通用类型算法会考虑每个候选类型,并选择与所有其他候选类型兼容的类型。

由于必须从提供的候选类型中选择最佳公共类型,因此在某些情况下类型共享公共结构,但没有一种类型是所有候选类型的超类型。例如:

ts
let zoo = [new Rhino(), new Elephant(), new Snake()];
    
// let zoo: (Rhino | Elephant | Snake)[]

理想情况下,我们可能希望zoo被推断为 an ,但由于数组中Animal[]没有严格类型的对象,因此我们不对数组元素类型进行推断。Animal要纠正此问题,请在没有一种类型是所有其他候选类型的超类型时显式提供类型:

ts
let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()];
    
let zoo: Animal[]

当未找到最佳通用类型时,得出的推断是联合数组类型(Rhino | Elephant | Snake)[]。

上下文打字 ​

在某些情况下,类型推断在 TypeScript 中也适用于“另一个方向”。这称为“上下文打字”。当表达式的类型由其位置隐含时,就会发生上下文类型化。例如:

ts
window.onmousedown = function (mouseEvent) {
  console.log(mouseEvent.button);
  console.log(mouseEvent.kangaroo);// Property 'kangaroo' does not exist on type 'MouseEvent'.Property 'kangaroo' does not exist on type 'MouseEvent'.
};

在这里,TypeScript 类型检查器使用函数的类型Window.onmousedown来推断赋值右侧函数表达式的类型。当它这样做时,它能够推断出参数的类型mouseEvent,该参数确实包含button属性,但不包含kangaroo属性。

这是有效的,因为 window 已经onmousedown在其类型中声明了:

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 events
interface GlobalEventHandlers {
  onmousedown: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
  // ...
}

TypeScript 足够智能,可以推断其他上下文中的类型:

ts
window.onscroll = function (uiEvent) {
  console.log(uiEvent.button);Property 'button' does not exist on type 'Event'.Property 'button' does not exist on type 'Event'.
};

基于上述函数被分配给 的事实Window.onscroll,TypeScript 知道这uiEvent是一个UIEvent,而不是像前面的示例那样的MouseEvent 。UIEvent对象不包含button属性,因此 TypeScript 会抛出错误。

如果此函数不在上下文类型位置,则该函数的参数将隐式具有 type any,并且不会发出错误(除非您使用该noImplicitAny选项):

ts
const handler = function (uiEvent) {
  console.log(uiEvent.button); // <- OK
};

我们还可以显式地向函数的参数提供类型信息以覆盖任何上下文类型:

ts
window.onscroll = function (uiEvent: any) {
  console.log(uiEvent.button); // <- Now, no error is given
};

但是,此代码将记录undefined,因为uiEvent没有名为 的属性button。

上下文类型适用于许多情况。常见情况包括函数调用的参数、赋值的右侧、类型断言、对象和数组文字的成员以及返回语句。上下文类型还充当最佳常见类型中的候选类型。例如:

ts
function createZoo(): Animal[] {
  return [new Rhino(), new Elephant(), new Snake()];
}

在此示例中,最佳常见类型有一组四个候选类型:Animal、Rhino、Elephant和Snake。其中,Animal可以通过最佳通用类型算法来选择。