# 类型推断
在 TypeScript 中,有几个地方在没有显式类型注释的情况下使用类型推断来提供类型信息。例如,在这段代码中
let x = 3;
x
变量的类型被推断为 number
。这种推断发生在初始化变量和成员、设置参数默认值以及确定函数返回类型时。
在大多数情况下,类型推断很简单。在接下来的部分中,我们将探讨如何推断类型的一些细微差别。
# 最佳常见类型
当从多个表达式进行类型推断时,这些表达式的类型用于计算 "best common type"。例如,
let x = [0, 1, null];
要推断上例中 x
的类型,我们必须考虑每个数组元素的类型。在这里,我们为数组的类型提供了两种选择:number
和 null
。最佳通用类型算法会考虑每种候选类型,并选择与所有其他候选类型兼容的类型。
因为必须从提供的候选类型中选择最佳公共类型,所以在某些情况下类型共享公共结构,但没有一种类型是所有候选类型的超类型。例如:
class Animal {}
class Rhino extends Animal {
hasHorn: true;
}
class Elephant extends Animal {
hasTrunk: true;
}
class Snake extends Animal {
hasLegs: false;
}
let zoo = [new Rhino(), new Elephant(), new Snake()];
理想情况下,我们可能希望将 zoo
推断为 Animal[]
,但由于数组中没有严格属于 Animal
类型的对象,因此我们不推断数组元素类型。为了纠正这个问题,当没有一种类型是所有其他候选者的超类型时,显式提供类型:
class Animal {}
class Rhino extends Animal {
hasHorn: true;
}
class Elephant extends Animal {
hasTrunk: true;
}
class Snake extends Animal {
hasLegs: false;
}
let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()];
当没有找到最佳公共类型时,结果推断是联合数组类型 (Rhino | Elephant | Snake)[]
。
# 上下文打字
在某些情况下,类型推断在 "the other direction" 中也适用于 TypeScript。这被称为 "contextual typing"。当表达式的类型由其位置隐含时,就会发生上下文类型。例如:
window.onmousedown = function (mouseEvent) {
console.log(mouseEvent.button);
console.log(mouseEvent.kangaroo);
};
在这里,TypeScript 类型检查器使用 Window.onmousedown
函数的类型来推断赋值右侧的函数表达式的类型。当它这样做时,它能够推断出 mouseEvent
参数的 类型
,它确实包含 button
属性,但不包含 kangaroo
属性。
这是有效的,因为 window 已经在其类型中声明了 onmousedown
:
// 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 足够聪明,可以在其他上下文中推断类型:
window.onscroll = function (uiEvent) {
console.log(uiEvent.button);
};
基于上面的函数被分配给 Window.onscroll
的事实,TypeScript 知道 uiEvent
是一个 UIEvent
,而不是像前面的例子那样的一个 MouseEvent
。UIEvent
对象不包含 button
属性,因此 TypeScript 会抛出错误。
如果此函数不在上下文类型位置,则函数的参数将隐式具有类型 any
,并且不会触发错误(除非您使用 noImplicitAny
选项):
const handler = function (uiEvent) {
console.log(uiEvent.button); // <- OK
};
我们还可以显式地为函数的参数提供类型信息以覆盖任何上下文类型:
window.onscroll = function (uiEvent: any) {
console.log(uiEvent.button); // <- Now, no error is given
};
但是,此代码将记录 undefined
,因为 uiEvent
没有名为 button
的属性。
上下文类型适用于许多情况。常见情况包括函数调用的参数、赋值的右侧、类型断言、对象和数组字面量的成员以及返回语句。上下文类型还充当最佳通用类型中的候选类型。例如:
class Animal {}
class Rhino extends Animal {
hasHorn: true;
}
class Elephant extends Animal {
hasTrunk: true;
}
class Snake extends Animal {
hasLegs: false;
}
function createZoo(): Animal[] {
return [new Rhino(), new Elephant(), new Snake()];
}
在此示例中,最佳常见类型具有一组四个候选:Animal
、Rhino
、Elephant
和 Snake
。其中,Animal
可以通过最佳通用类型算法来选择。