TS -- (3)声明文件、内置对象、类型别名、字符串字面量类型、元组、枚举
2019-10-28:
学习内容:声明文件、内置对象、类型别名、字符串字面量类型、元组、枚举
补充:
(1)搜索需要的声明文件:https://microsoft.github.io/TypeSearch/
一、声明文件:
当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。
(1)声明语句:
假如我们想使用第三方库 jQuery,一种常见的方式是在 html 中通过 <script>
标签引入 jQuery,然后就可以使用全局变量 $
或 jQuery
了。但是在 ts 中,编译器并不知道 $
或 jQuery
是什么东西。这时,我们需要使用 declare var
来定义它的类型:
declare var jQuery: (selector: string) => any; jQuery('#foo'); // declare var 并没有真的定义一个变量,只是定义了全局变量 jQuery 的类型,仅仅会用于编译时的检查,在编译结果中会被删除。它编译结果是:jQuery('#foo');
(2)声明文件:
通常我们会把声明语句放到一个单独的文件(jQuery.d.ts
)中,这就是声明文件
声明文件必需以 .d.ts
为后缀。
ts 会解析项目中所有的 *.ts
文件,当然也包含以 .d.ts
结尾的文件。所以当我们将 jQuery.d.ts
放到项目中时,其他所有 *.ts
文件就都可以获得 jQuery
的类型定义了。
假如是通过模块导入的方式使用第三方库的话,那么引入声明文件又是另一种方式了。
(3)第三方声明文件(常用):
jquery的声明文件:https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/jquery/index.d.ts
使用 @types
统一管理第三方库的声明文件。
@types
的使用方式很简单,直接用 npm 安装对应的声明模块即可,以 jQuery 举例:npm install @types/jquery --save-dev
这个链接搜索需要的声明文件:https://microsoft.github.io/TypeSearch/
*搜索结果举例:
(4)如何书写声明文件:这里不做记录,详情请看:https://ts.xcatliu.com/basics/declaration-files
二、内置对象:
内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。
(1)ECMAScript 标准提供的内置对象有:Boolean
、Error
、Date
、RegExp
等。TS中可以直接使用
let b: Boolean = new Boolean(1); let e: Error = new Error('Error occurred'); let d: Date = new Date(); let r: RegExp = /[a-z]/;
(2)DOM 和 BOM 提供的内置对象有:Document
、HTMLElement
、Event
、NodeList
等。
let body: HTMLElement = document.body; let allDiv: NodeList = document.querySelectorAll('div'); document.addEventListener('click', function(e: MouseEvent) { // Do something });
(3)TS核心库的定义文件:
TypeScript 核心库的定义文件中定义了所有浏览器环境需要用到的类型,并且是预置在 TypeScript 中的。当你在使用一些常用的方法的时候,TypeScript 实际上已经帮你做了很多类型判断的工作了。
举例:
Math.pow(10, '2'); // index.ts(1,14): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number' interface Math { /** * Returns the value of a base expression taken to a specified power. * @param x The base value of the expression. * @param y The exponent value of the expression. */ pow(x: number, y: number): number; }
注意:TypeScript 核心库的定义中不包含 Node.js 部分
三、类型别名:
类型别名用来给一个类型起个新名字。
四、字符串字面量类型:
字符串字面量类型用来约束取值只能是某几个字符串中的一个。
type EventNames = 'click' | 'scroll' | 'mousemove'; function handleEvent(ele: Element, event: EventNames) { // do something } handleEvent(document.getElementById('hello'), 'scroll'); // 没问题 handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick' // index.ts(7,47): error TS2345: Argument of type '"dbclick"' is not assignable to parameter of type 'EventNames'.
注意,类型别名与字符串字面量类型都是使用 type
进行定义
五、元组:
数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。
元组起源于函数编程语言(如 F#),这些语言中会频繁使用元组。
例子1: 给元组赋值
// 直接赋值:需要提供所有元组类型中指定的项 let tom: [string, number] = ['Tom', 25]; // 分开赋值:可以只赋值其中一项 let tom: [string, number]; tom[0] = 'Tom'; tom[1] = 25; tom[0].slice(1); tom[1].toFixed(2);
例子2: 越界的元素(大于定义的数量,这些元素只能符合联合类型)
let tom: [string, number]; tom = ['Tom', 25]; tom.push('male'); tom.push(true); // Argument of type 'true' is not assignable to parameter of type 'string | number'.
六、枚举(Enum):
(1)普通枚举(数字枚举,字符串枚举):
枚举(Enum)类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等
例子1: 使用枚举及编译结果
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; console.log(Days["Sun"] === 0); // true console.log(Days["Mon"] === 1); // true console.log(Days["Tue"] === 2); // true console.log(Days["Sat"] === 6); // true console.log(Days[0] === "Sun"); // true console.log(Days[1] === "Mon"); // true console.log(Days[2] === "Tue"); // true console.log(Days[6] === "Sat"); // true // 编译结果: var Days; (function (Days) { Days[Days["Sun"] = 0] = "Sun"; Days[Days["Mon"] = 1] = "Mon"; Days[Days["Tue"] = 2] = "Tue"; Days[Days["Wed"] = 3] = "Wed"; Days[Days["Thu"] = 4] = "Thu"; Days[Days["Fri"] = 5] = "Fri"; Days[Days["Sat"] = 6] = "Sat"; })(Days || (Days = {}));
例子2: 手动赋值
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat}; console.log(Days["Sun"] === 7); // true console.log(Days["Mon"] === 1); // true console.log(Days["Tue"] === 2); // true console.log(Days["Sat"] === 6); // true // 未手动赋值的枚举项会接着上一个枚举项递增。 // 如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 是不会察觉到这一点的 enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat}; console.log(Days["Sun"] === 3); // true console.log(Days["Wed"] === 3); // true console.log(Days[3] === "Sun"); // false console.log(Days[3] === "Wed"); // true
递增到 3
的时候与前面的 Sun
的取值重复了,但是 TypeScript 并没有报错,导致 Days[3]
的值先是 "Sun"
,而后又被 "Wed"
覆盖了。
// 编译结果: var Days; (function (Days) { Days[Days["Sun"] = 3] = "Sun"; Days[Days["Mon"] = 1] = "Mon"; Days[Days["Tue"] = 2] = "Tue"; Days[Days["Wed"] = 3] = "Wed"; Days[Days["Thu"] = 4] = "Thu"; Days[Days["Fri"] = 5] = "Fri"; Days[Days["Sat"] = 6] = "Sat"; })(Days || (Days = {}));
注意:最好不要出现这种覆盖的情况。
例子3: 手动赋值可以不是数字
enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = <any>"S"}; // 编译后: var Days; (function (Days) { Days[Days["Sun"] = 7] = "Sun"; Days[Days["Mon"] = 8] = "Mon"; Days[Days["Tue"] = 9] = "Tue"; Days[Days["Wed"] = 10] = "Wed"; Days[Days["Thu"] = 11] = "Thu"; Days[Days["Fri"] = 12] = "Fri"; Days[Days["Sat"] = "S"] = "Sat"; })(Days || (Days = {}));
例子4: 手动赋值的枚举项也可以为小数或负数,此时后续未手动赋值的项的递增步长仍为 1
enum Days {Sun = 7, Mon = 1.5, Tue, Wed, Thu, Fri, Sat}; console.log(Days["Sun"] === 7); // true console.log(Days["Mon"] === 1.5); // true console.log(Days["Tue"] === 2.5); // true console.log(Days["Sat"] === 6.5); // true
(2)枚举项的两种类型:
常数项(constant member)和计算所得项(computed member)
前面的例子都是常数项,一个典型的计算所得项例子:
enum Color {Red, Green, Blue = "blue".length};
但是,如果紧接在计算所得项后面的是未手动赋值的项,那么它就会因为无法获得初始值而报错。计算所得项要放最后。
《中文手册》:
当满足以下条件时,枚举成员被当作是常数:
-
不具有初始化函数并且之前的枚举成员是常数。在这种情况下,当前枚举成员的值为上一个枚举成员的值加
1
。但第一个枚举元素是个例外。如果它没有初始化方法,那么它的初始值为
0
。 -
枚举成员使用常数枚举表达式初始化。常数枚举表达式是 TypeScript 表达式的子集,它可以在编译阶段求值。
当一个表达式满足下面条件之一时,它就是一个常数枚举表达式:
-
数字字面量
-
引用之前定义的常数枚举成员(可以是在不同的枚举类型中定义的)如果这个成员是在同一个枚举类型中定义的,可以使用非限定名来引用
-
带括号的常数枚举表达式
-
+
,-
,~
一元运算符应用于常数枚举表达式 -
+
,-
,*
,/
,%
,<<
,>>
,>>>
,&
,|
,^
二元运算符,常数枚举表达式做为其一个操作对象。若常数枚举表达式求值后为 NaN 或 Infinity,则会在编译阶段报错
-
所有其它情况的枚举成员被当作是需要计算得出的值。
(3)常数枚举(const 枚举):
常数枚举与普通枚举的区别是,它会在编译阶段被删除,并且不能包含计算成员。为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问
使用 const enum
定义。
常量枚举只能使用常量枚举表达式,并且不同于常规的枚举,它们在编译阶段会被删除。 常量枚举成员在使用的地方会被内联进来。 之所以可以这么做是因为,常量枚举不允许包含计算成员。
例子:
const enum Directions { Up, Down, Left, Right } let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]; // 编译结果为: var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
(4)外部枚举:
外部枚举与声明语句一样,常出现在声明文件中。
使用 declare enum
定义。declare
定义的类型只会用于编译时的检查,编译结果中会被删除。
declare enum Directions { Up, Down, Left, Right } let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]; // 编译结果: var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
- const和declare同时使用也是可以的:
declare const enum Directions { Up, Down, Left, Right } let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]; // 编译结果: var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];