类型推导
状态: 初稿
介绍
在这一章, 我们将探讨 TypeScript 类型推导机制. 包括两个主题: 1. 类型推导在何处发生, 2. 具体推导方法.
基础
在 TypeScript 中, 类型推导的作用是为缺少显式类型注解的语境补全类型信息.
考虑这个例子
1 | let x = 3; |
类型推导推出变量 x
的类型是 number
.
这种类型的推导发生在初始化变量和成员变量时, 指定函数参数默认值时, 以及定出函数返回值类型时.
在多数情况下, 类型推导足够简单直接.
以下各节. 我们将更细致地探索类型推导方法.
最佳公共类型
如果类型推导的输入是多个表达式, 它需要从这些子表达式的类型中找出一个 “最佳公共类型”. 如:
1 | let x = [0, 1, null]; |
要推导 x
, 我们必须考虑每个数组元素类型.
易知, 共有 number[]
和 null[]
两种选择.
“最佳公共类型”算法考虑每个候选类型, 选出与所有候选相容的那一个.
由于算法从所有候选类型中挑选最佳公共类型, 有种情况是所有候选类型共享一部分结构, 但是没有一个类型是所有类型的父类型. 例如:
1 | let zoo = [new Rhino(), new Elephant(), new Snake()]; |
zoo
理想推导结果应该是 Animal[]
, 但数组中没有一个元素是严格意义上的 Animal
, 类型推导无法给出我们想要的结果.
此时, 最好显式为 zoo
提供类型信息:
1 | let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()]; |
上下文类型
类型推导有时也反方向地进行.
这叫做”上下文类型”. 上下文类型推导的依据是表达式的类型已由它所处位置体现. 例如:
1 | window.onmousedown = function(mouseEvent) { |
这里, TypeScript 类型检查器根据类型已知的 window.onmousedown
成员自动推导赋值号右侧的函数表达式.
函数参数 mouseEvent
的类型随函数表达式类型的确定而明确下来, 该参数有 button
属性, 没有 kangaroo
属性.
换个场景, TypeScript 同样聪明:
1 | window.onscroll = function(uiEvent) { |
基于以上函数表达式赋值给 window.onscroll
的事实, TypeScript 知道 uiEvent
的类型是 UIEvent, 而不再前一个例子中的 MouseEvent. 因为 UIEvent
没有 button
属性, TypeScript 将报错.
如果函数所处位置不含上下文类型信息, any
隐式地成为所有函数参数的类型, 编译器不会报错(除非你开启 --noImplicitAny
选项).
1 | const handler = function(uiEvent) { |
我们可以显式写下函数参数类型, 从而阻止上下文类型推导:
1 | window.onscroll = function(uiEvent: any) { |
然而, 由于 uiEvent
实际上没有叫 button
的属性, 这个函数会输出 undefined
.
上下文类型覆盖 TypeScript 程序的方方面面.
常见的有函数实参, 右值, 类型担保, 类成员和数组字面量, return 语句.
上下文类型推导丰富了最佳公共类型的候选. 对于下例:
1 | function createZoo(): Animal[] { |
在这个例子中, 最佳公共类型有四个候选: Animal
, Rhino
, Elephant
, 和 Snake
.
我们知道, 现在最佳公共类型算法可以选择 Animal
.