# 类型推断

原文地址 (opens new window)

在本教程中,你将学习 TypeScript 中的类型推断

类型推断描述的是当你没有为变量添加 类型注释 的时候,TypeScript 是如何推断变量的类型的。

# 类型推断基础

当你声明一个变量的时候,你可以使用 类型注释 为变量指定某种类型,如下所示:

let counter: number;
1

当你把 counter 变量初始化一个数字的时候,TypeScript 会推断 counter 的类型为 数字类型,如下所示:

let counter = 0;
1

它和下面的语句是等价的:

let counter: number = 0;
1

同样的,当你给函数参数赋值的时候,TypeScript 将参数的类型推断为默认值的类型,比如:

function setCounter(max = 100) {
  // ...
}
1
2
3

在这个例子中,TypeScript 推断 max 参数的类型为 number 类型。类似地,TypeScript 推断下面 increment() 函数的返回值类型为 number 类型:

function increment(counter: number) {
  return counter++;
}
1
2
3

它和下面例子是等价的:

function increment(counter: number): number {
  return counter++;
}
1
2
3

# 最佳通用类型算法

思考下面的语句:

let items = [1, 2, 3, null];
1

为了推断出 items 变量的类型,TypeScript 需要考虑数组中的每个元素的类型。TypeScript 使用最佳通用类型算法来分析每个候选类型,最终选择与所有候选类型都兼容的类型作为变量的类型。

在这个例子中,TypeScript 选择数字 数组类型 (number[]) 作为最佳通用类型。如果你往 items 数组中添加一个字符串,TypeScript 会把 items 变量的类型推断为数字与字符串组合的数组类型,即 (number | string)[]

let items = [0, 1, null, 'Hi'];
1

当 TypeScript 找不到最佳通用类型的时候,它会返回联合数组类型,如下所示:

let arr = [new Date(), new RegExp('d+')];
1

在这个例子中,TypeScript 推断 arr 变量的类型为 (RegExp | Date)[]

# 上下文类型

TypeScript 可以使用位置上下文推断变量的类型,这种机制被称为上下文类型,如下所示:

document.addEventListener('click', function (event) {
  console.log(event.button); //
});
1
2
3

在这个例子中,由于 event 参数在 click 事件中,TypeScript 知道它是 MouseEvent 的一个实例。

然而,当你把 click 事件修改成 scroll 事件,TypeScript 会抛出一个错误提示:

document.addEventListener('scroll', function (event) {
  console.log(event.button); // compiler error
});
1
2
3

错误提示:

Property 'button' does not exist on type 'Event'.(2339)
1

TypeScript 知道在这种情况下 event 不再是 MouseEvent 的实例,而是 UIEvent 的实例。而由于 UIEvent 没有 button 属性,访问 MouseEvent 实例的 button 属性的时候 TypeScript 会抛出了一个错误提示。

很多情况中可以找到上下文类型的踪影,比如函数调用的参数,类型断言,对象和数组的成员,返回语句和右侧赋值。

# 类型推断 vs 类型注释

下面展示了类型推断和类型注释的区别:

类型推断 类型注释
TypeScript 会猜测变量的类型 明确告诉 TypeScript 变量的类型

所以,应该在什么时候使用类型推断,什么时候使用类型注释?实际上,你应该尽可能的使用类型推断,只有在下面的几种情况中才去使用类型注释:

  • 当声明一个变量但在后面才为它赋值的时候;
  • 当你希望一个变量的类型不能被推断的时候;
  • 当一个函数的返回类型是 any 类型,而你需要明确知道它的类型的时候。

# 小结

  • 类型推断发生在初始化变量的值,设置参数默认值和需要确定函数返回值类型的时候;
  • TypeScript 使用最佳通用类型算法来选择与所有变量类型兼容的最佳候选类型;
  • TypeScript 还根据变量的位置,使用上下文类型来推断变量的类型。