二、类型推断

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

// let x: number
let x = 3;

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

在大多数情况下,类型推断是直截了当的。在下面的章节中,我们将探讨类型推断的一些细微差别。

2.1 最佳公共类型

当从几个表达式中进行类型推断时,这些表达式的类型被用来计算一个 “最佳公共类型”。比如说:

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

为了推断上面例子中 x 的类型,我们必须考虑每个数组元素的类型。这里我们得到了两个数组类型的选择:numbernull。最佳公共类型算法考虑了每个候选类型,并选择了与所有其他候选类型兼容的类型。

因为最佳公共类型必须从所提供的候选类型中选择,所以在某些情况下,类型有共同的结构,但没有一个类型是所有候选类型的超级类型。比如说:

// let zoo: (Rhino | Elephant | Snake)[]

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

理想情况下,我们可能希望 zoo 被推断为 Animal[],但是因为数组中没有严格意义上的 Animal 类型的对象,所以我们没有对数组元素类型进行推断。为了纠正这一点,当没有一个类型是所有其他候选类型的超级类型时,就明确地提供类型。

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

当没有找到最好的共同类型时,产生的推论是联合数组类型,(Rhino | Elephant | Snake)[]

2.2 上下文类型

在TypeScript的某些情况下,类型推理也在 “另一个方向 “发挥作用。这被称为 “上下文类型化”。当表达式的类型被它的位置所暗示时,上下文类型就发生了。比如说:

window.onmousedown = function (mouseEvent) {
console.log(mouseEvent.button);
console.log(mouseEvent.kangaroo); // Ⓧ 在'MouseEvent'类型上不存在'kangaroo'属性。
};

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

这样做的原因是window已经在其类型中声明了onmousedown

// 声明有一个名为'window'的全局变量
declare var window: Window & typeof globalThis;

// 这被声明为(简化版)。
interface Window extends GlobalEventHandlers {
// ...
}

// 其中定义了很多已知的处理程序事件
interface GlobalEventHandlers {
onmousedown: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null;
// ...
}

TypeScript足够聪明,在其他情况下也能推断出类型:

window.onscroll = function (uiEvent) {
// Ⓧ 属性 "button" 不存在于 "Event"类型上。
console.log(uiEvent.button);
};

基于上述函数被分配给Window.onscroll的事实,TypeScript知道uiEvent是一个UIEvent,而不是像前面的例子那样是MouseEventUIEvent对象不包含按钮属性,所以TypeScript会抛出一个错误。

如果这个函数不在上下文类型的位置,这个函数的参数将隐含有类型any,并且不会发出错误(除非你使用noImplicitAny选项)。

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

我们也可以明确地给函数的参数提供类型信息,以覆盖任何上下文的类型。

window.onscroll = function (uiEvent: any) {
console.log(uiEvent.button); // <- 现在也没有错误
};

然而,这段代码将记录 undefined 的内容,因为 uiEvent 没有名为按钮的属性。

上下文类型化在很多情况下都适用。常见的情况包括函数调用的参数、赋值的右侧、类型断言、对象和数组字面量的成员,以及返回语句。上下文类型也作为最佳普通类型的候选类型。比如说:

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

在这个例子中,最佳普通类型有一组四个候选者。Animal, Rhino, ElephantSnake。其中,Animal 可以被最佳共同类型算法所选择。

特别声明: 本文转自 古艺散人老师 ,如有需要可前往原文预览查看。