TypeScript any 类型
本章介绍 TypeScript 的三种特殊类型,它们可以作为学习 TypeScript 类型系统的起点。
any 类型 #
基本含义 #
any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值。
let x:any;
x = 1; // 正确
x = 'foo'; // 正确
x = true; // 正确
上面示例中,变量x
的类型是any
,就可以被赋值为任意类型的值。
变量类型一旦设为any
,TypeScript 实际上会关闭这个变量的类型检查。即使有明显的类型错误,只要句法正确,都不会报错。
let x:any = 'hello';
x(1) // 不报错
x.foo = 100; // 不报错
上面示例中,变量x
的值是一个字符串,但是把它当作函数调用,或者当作对象读取任意属性,TypeScript 编译时都不报错。原因就是x
的类型是any
,TypeScript 不对其进行类型检查。
由于这个原因,应该尽量避免使用any
类型,否则就失去了使用 TypeScript 的意义。
实际开发中,any
类型主要适用以下两个场合。
(1)出于特殊原因,需要关闭某些变量的类型检查,就可以把该变量的类型设为any
。
(2)为了适配以前老的 JavaScript 项目,让代码快速迁移到 TypeScript,可以把变量类型设为any
。有些年代很久的大型 JavaScript 项目,尤其是别人的代码,很难为每一行适配正确的类型,这时你为那些类型复杂的变量加上any
,TypeScript 编译时就不会报错。
总之,TypeScript 认为,只要开发者使用了any
类型,就表示开发者想要自己来处理这些代码,所以就不对any
类型进行任何限制,怎么使用都可以。
从集合论的角度看,any
类型可以看成是所有其他类型的全集,包含了一切可能的类型。TypeScript 将这种类型称为“顶层类型”(top type),意为涵盖了所有下层。
类型推断问题 #
对于开发者没有指定类型、TypeScript 必须自己推断类型的那些变量,如果无法推断出类型,TypeScript 就会认为该变量的类型是any
。
function add(x, y) {
return x + y;
}
add(1, [1, 2, 3]) // 不报错
上面示例中,函数add()
的参数变量x
和y
,都没有足够的信息,TypeScript 无法推断出它们的类型,就会认为这两个变量和函数返回值的类型都是any
。以至于后面就不再对函数add()
进行类型检查了,怎么用都可以。
这显然是很糟糕的情况,所以对于那些类型不明显的变量,一定要显式声明类型,防止被推断为any
。
TypeScript 提供了一个编译选项noImplicitAny
,打开该选项,只要推断出any
类型就会报错。
$ tsc --noImplicitAny app.ts
上面命令使用了noImplicitAny
编译选项进行编译,这时上面的函数add()
就会报错。
这里有一个特殊情况,即使打开了noImplicitAny
,使用let
和var
命令声明变量,但不赋值也不指定类型,是不会报错的。
var x; // 不报错
let y; // 不报错
上面示例中,变量x
和y
声明时没有赋值,也没有指定类型,TypeScript 会推断它们的类型为any
。这时即使打开了noImplicitAny
,也不会报错。
let x;
x = 123;
x = { foo: 'hello' };
上面示例中,变量x
的类型推断为any
,但是不报错,可以顺利通过编译。
由于这个原因,建议使用let
和var
声明变量时,如果不赋值,就一定要显式声明类型,否则可能存在安全隐患。
const
命令没有这个问题,因为 JavaScript 语言规定const
声明变量时,必须同时进行初始化(赋值)。
const x; // 报错
上面示例中,const
命令声明的x
是不能改变值的,声明时必须同时赋值,否则报错,所以它不存在类型推断为any
的问题。
污染问题 #
any
类型除了关闭类型检查,还有一个很大的问题,就是它会“污染”其他变量。它可以赋值给其他任何类型的变量(因为没有类型检查),导致其他变量出错。
let x:any = 'hello';
let y:number;
y = x; // 不报错
y * 123 // 不报错
y.toFixed() // 不报错
上面示例中,变量x
的类型是any
,实际的值是一个字符串。变量y
的类型是number
,表示这是一个数值变量,但是它被赋值为x
,这时并不会报错。然后,变量y
继续进行各种数值运算,TypeScript 也检查不出错误,问题就这样留到运行时才会暴露。
污染其他具有正确类型的变量,把错误留到运行时,这就是不宜使用any
类型的另一个主要原因。