从 0 开始的 TypeScriptの三:TS 的类型
上文《从0开始的TypeScriptの二:类型系统》中说过,JavaScript 有的基础类型在TypeScript
中都存在。
本次来介绍一些TypeScript
中新的类型, 有元组,枚举,任意值,Unknown,空值,Never
在TypeScript
对于类型系统的使用,还存在类型别名和类型断言等用法。
元组 Tuple
之前写数组的过程中,肯定会想,如果我不用后缀类型注释,是不是就可以写不同类型组成的数组了。
但这在 TypeScript 中如此定义,就是另一个数据类型了,叫做元组 Tuple
元组是 JavaScript 当中不存在的数据类型
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
TS:
JS:
从上面的例子来看,元组就相当于一个长度,每一项元素类型都确定的数组。
使用后缀类型注释时确定好的格式如果在赋值时不符,编译时会出现报错
枚举 enum
枚举类型同样是 JavaScript 中没有的
枚举类型用于取值被限定在一定范围内的场景。比如设定日期为周一到周日
TS:
JS:
使用 node 运行 js 文件来打印 day
。
自动赋值
枚举成员会被自动赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射
手动赋值
枚举成员也可以进行手动赋值
这样一来,编译后的结果(成员后面没有手动赋值的,会从当前成员的值开始递增):
注意: 在手动赋值时,有可能会造成重复的情况
上面的例子中,Mon 的值会与 Wed 和 Fri 值重复,都为 3。 Thu 和 Sat 的值都为 4.
所以最好不要出现这种覆盖的情况。
另外,手动赋值的枚举项可以不是数字,但是这需要使用类型断言让 tsc 无视类型检查
常数项和计算所得项
枚举项有两种类型:
常数项(constant member)
计算所得项(computed member)
之前定义的枚举值都是常数项,如果是计算所得项:
'hello'.length
属于计算所得项, 1 + 1
属于常数项。
但是这里有一个缺陷,如果在计算所得项之后是 未手动赋值的项
, 那么就会因为无法活得初始值而报错
比如:enum key { key1 = 1 + 1, key2 = 'hello'.length, key3 }
。会报错:error TS1061: Enum member must have initializer.
枚举项被当作常数项的条件:
不具有初始化函数并且之前的枚举成员是数字常数。 在这种情况下,当前枚举成员的值为上一个枚举成员的值加 1。 第一个枚举成员除外,如果没有初始化方法,那么初始值为 0
枚举成员使用常数枚举表达式初始化。常数枚举表达式是 TypeScript 表达式的子集, 可以在编译阶段求值。 当表达式满足下面条件之一时,就是一个常数枚举表达式:
数字字面量
引用之前定义的常数枚举成员(可以是在不同枚举类型中定义的),如果这个成员是在同一个枚举类型定义的,可以使用非限定名来引用
带括号的常数枚举表达式
+,-,~等一元运算符应用在常数枚举表达式
+,-,*,/,%,<<,>>,>>>,&,|,^二元运算符,常数枚举表达式若求值后为 NaN 或 Infinity,则会在编译阶段报错,上面的
1 + 1
就是在编译阶段求值
数字枚举
之前自动赋值形成的枚举就属于数字枚举
数字枚举是枚举项都是数字常量的枚举
<br>
字符串枚举
字符串枚举中,每一个枚举成员都必须使用字符串字面量或者另一个字符串枚举成员进行初始化
常数枚举
常数枚举和上面讲到的常数项不是同一种东西
常数枚举是使用 const enum 定义的枚举类型
常数枚举会在编译阶段被删除,并且不能包含计算项
例子:TS:
在 tsc 编译出的 js 中没有找到 cenum 的声明,并且如果使用计算项,会出现报错error TS2474: const enum member initializers can only contain literal values and other computed enum values
外部枚举 Ambient Enums
外部枚举是使用declare enum
定义的枚举类型
与常数枚举相同,外部枚举也在编译阶段被删除
因为之前提到过,declare
定义的类型只会用于编译时的检查,在编译结果中会被删除
例子:TS
编译后:JS
外部枚举经常出现在声明文件中,并且可以和常数枚举同时使用 declare const enum
<br><br>
任意值 any
任意值 any 也是 JavaScript 中不存在的类型
在编程阶段,可能会遇到变量不知道会是什么类型的情况, 比如这些变量的值可能来自动态内容
这种情况下,就需要我们关闭类型检查器,不然会出现一堆类型报错。
any
在 TypeScript 类型系统中又特殊地位, 它与类型系统中的任何类型都兼容。意味着可以将任何内容赋值给它,也可以将它赋值给任何类型。它能让你避开类型检查。
TS 例子:
运行 tsc 后,编译不会有报错。 使用typeof
可以判断变量类型
当需要从 JavaScript 迁移代码到 TypeScript 时,会经常使用any
。 但是必须减少对它的依赖,因为这是类型安全的要求。
Unknown
TypeScript 3.0 引入了一个顶级的unknown类型
。 对照于any
,unknown
是类型安全的, 或者说unknown
是any
的安全版本,在使用any
前,可以先尝试使用unknown
。
在
any
允许我们做任何事的地方,unknown
的限制则大得多
当我们在写应用的时候可能会需要描述一个我们还不知道其类型的变量。这些值可以来自动态内容,例如从用户获得,或者我们想在我们的 API 中接收所有可能类型的值。在这些情况下,我们想要让编译器以及未来的用户知道这个变量可以是任意类型。这个时候我们会对它使用 unknown 类型。
unknown
限制:当没有类型断言或基于控制流的类型细化时 unknown 不可以赋值给其它类型
例子:
如果给 a 附加了类型断言,就不会出现报错问题
或者使用typeof
进行类型防护
空值 void
空值void
类型与any
相反,表示没有任何类型。
通常会用void
表示一个函数没有返回值;
Never
never
类型表示的是那些永不存在的值的类型
never
类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never
的子类型或可以赋值给never
类型(除了never
本身之外)。
即使 any 也不可以赋值给never
例子:
不能将除 never 以外的类型赋值给 never
类型别名 type
类型别名用来给一个类型起个新名字。
类型断言
类型断言(Type Assertion
)可以用来手动指定一个值的类型。
使用语法有两种:
as 类型
<类型>
可以给联合类型只访问其中一个类型
需要注意的是,类型断言只能够 「欺骗」 TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误
版权声明: 本文为 InfoQ 作者【空城机】的原创文章。
原文链接:【http://xie.infoq.cn/article/30598bd8be64c24e0721942d9】。文章转载请联系作者。
评论