在某些没有显式提供类型注释的地方,TypeScript 会通过叫做“类型推断”的方式提供类型信息,比如这个例子:
let x = 3; // Type: number
变量 x 的类型被推断为 number,在初始化变量值、初始化属性值、设置函数参数默认值、决定函数返回值等场景中,这种推断方式就会介入。
大多数情况下,类型推断都比较简单直接,然而在个别情况下,也会有一些细微的区别。
最通用类型(Best common type)
当从多个表达式推断类型时,最终得到的类型是“最通用类型”,比如:
let x = [0, 1, null]; // Type: (number | null)[]
上面这个例子中,为了推断 x 的类型,必需考虑到数组中每一个成员的类型,这里数组中包含 number 和 null 两种类型,用于推断的成员类型也叫做候选类型,最终得到的类型需要兼容所有候选类型。
需要注意的是,最通用类型是严格从候选类型中选择的,所以,即使候选类型都拥有共同的父类,也无法做出父类的推断,比如:
let zoo = [new Rhino(), new Elephant(), new Snake()]; // Type: (Rhino | Elephant | Snake)[]
这个例子中,我们可能希望 zoo 的类型被推断为 Animal[],但由于候选类型中没有任何一个成员的类型是 Animal,也就无法做出这样的推断,如果想得到 x: Animal[] 的推断,请显式声明。
let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()];
上下文推断(Contexual Typing)
TypeScript 中还有一种叫做“上下文推断”的类型推断方式,当某个未显式声明类型的目标的类型可以由上下文类型推断出来时,这种推断方式就会生效。
window.onmousedown = function (mouseEvent) {console.log(mouseEvent.button);console.log(mouseEvent.kangaroo); // Error: Property 'kangaroo' does not exist on type 'MouseEvent'.};
这个例子中,TypeScript 会使用 window.onmousedown 的类型签名检验赋值符号右侧的函数。此时它能够知道右侧函数的 mouseEvent 参数类型是 MouseEvent,这个类型有 button 属性,但没有 kangaroo 属性,所以会报错。
当然,TypeScript 之所以这样做的前提是,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 eventsinterface GlobalEventHandlers {onmousedown: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;// ...}
在其它情况下,这种推断方式同样有效:
window.onscroll = function (uiEvent) {
console.log(uiEvent.button); // Error: Property 'button' does not exist on type 'Event'.
};
TypeScript 知道 uiEvent 的类型是 UIEvent,而不是 MouseEvent,UIEvent 没有 button 属性,所以报错。
但是,如果上述赋值符号右侧的函数无法建立有效的上下文推断信息,它的参数会被推断为 any,也就不会报错:
const handler = function (uiEvent) {
console.log(uiEvent.button); // <- OK
};
显式声明的类型优先级要高于类型推断,即使在有上下文信息的情况下,如果我们为函数参数显式声明了类型,TypeScript 会尊重我们的声明:
window.onscroll = function (uiEvent: any) {
console.log(uiEvent.button); // <- Now, no error is given
};
实际上 console.log(uiEvent.button) 会输出 undefined,因为 uiEvent 并没有 button 属性。
常见的上下文推断发生场景包括但不限于函数调用的参数、赋值、类型断言、对象和数组字面量、返回声明等。
上下文类型在最通用类型推断中也充当候选类型:
function createZoo(): Animal[] {
return [new Rhino(), new Elephant(), new Snake()];
}
这个例子中,最通用类型的候选类型包括:Animal、Rhino、Elephant、Snake,Animal 当然是可以作为最通用类型的。
