学习资源来自:
类与接口 · TypeScript 入门教程 (xcatliu.com)
一.TypeScript的安装和运行
1.安装TypeScript
- 通过npm(Node.js包管理器)
- 安装Visual Studio的TypeScript插件:(Visual Studio 2017和Visual Studio 2015 Update 3默认包含了TypeScript。 如果你的Visual Studio还没有安装TypeScript)
2.npm安装TypeScript
- 安装教程 TypeScript环境搭建,并且部署到VSCode(亲测有效)_咖啡壶子的博客-CSDN博客
- 安装过程中问题:
TypeScript- 解决(tsc 不是内部或外部命令,也不是可运行的程序或批处理文件)问题 - sanyekui - 博客园 (cnblogs.com)
3.将环境安装好之后,就可以尝试构建第一个typeScript程序
- 新建Demo.ts文件
function greeter(person: string) {return 'Hello, ' + person
}let user = 'Jane User'document.body.innerHTML = greeter(user)
- 在终端输入:
tsc greeter.ts
- 就会输出一个 包含和输入内容一样的JavaScript代码
- 新建文件demo.html
<!DOCTYPE html>
<html><head><title>TypeScript Greeter</title></head><body><script src="demo.js"></script></body>
</html>
- 执行结果会展示在界面上
二.基础类型
1.布尔值
- true 或者 false
- 在JS或者TS中叫boolean
- 语法: let boolean = false
- 注意:使用Boolean 创造的对象不是布尔值,比如:let createdByNewBoolean: Boolean = new Boolean(1);
- 直接调用Boolean 也可以返回一个 boolean类型 let createdByBoolean: boolean = Boolean(1);
- 在TypeScript中,boolean 是基本类型,Boolean 是构造函数
2.数字类型
- TypeScript里面所有数字都是浮点数,这些浮点数的类型是Number
- 除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015 中引入的二进制和八进制
- 二进制表示法:let binaryLiteral: number = 0b1010;
- 八进制表示法:let octalLiteral: number = 0o744;
- 二进制和八进制会被编译为十进制数字
3.字符串
- 使用string表示
- 用单引号或者双引号包起来
- 可以使用模板字符串 ${`XXX`}
4.数组
- let list:number[] = [1, 2, 3]
- let list:Array<number> = [1, 2, 3]
5.元组Tuple
- 表示一个已知元素数量和类型的数组
- 各个元素的类型不一定相同
- let x:[string, number]
- x = ['hello', 10]
6.枚举
- enum类型是对JavaScript标准数据类型的一个补充
enum Color {Red,Green,Blue,
}
let c: Color = Color.Green
-
默认情况下,从0开始为元素编号,可以手动指定成员的数值
enum Color {Red = 1,Green,Blue,
}
let c: Color = Color.Green
console.log(c) // 2
-
全部采用手动赋值
enum Color {Red = 1,Green = 2,Blue = 4,
}
let c: Color = Color.Blue
console.log(c) // 4
-
找出映射的名字
enum Color {Red = 1,Green,Blue,
}
let colorName: string = Color[2]
console.log(colorName) // Green
7.任意值 any
- 需要在编译阶段还不清楚的变量指定一个类型
- 不希望类型检查器对这些值进行检查,而是直接让它们通过编译阶段的检查
- 就可以使用any类型来标记这些变量
- 注意:Object类型的变量只是允许你给他赋任意值,却不能够在它上面调用任意的方法
- 当你只知道一部分数据的类型时,any类型也是有用的, 比如:
let list: any[] = [1, true, "free"]
-
声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值
-
在声明的之后,未指定其类型,就会被识别为任意值类型
8.空值 void
- 表示没有任何类型
- 当一个函数没有返回值时,就会见到void
- 声明一个void变量没什么用,因为你只能赋予它undefined 和 null
9.null和undefined
- 与void的区别是,undefined和null是所有类型的子类型,也就是说undefined类型的变量可以赋值给所有类型
- void的类型不可以
10.never
- never类型标识永不存在的值的类型
- never类型是那些总是会抛出异常,或者根本不会有返回值的函数表达式或箭头函数表达式的返回值类型
- 变量也可能是never类型,当它们被永不为真的类型保护所约束的时候
- never类型是任何类型的子类型:可以赋值给任何类型
- 没有类型是never的子类型,就是只能将never类型赋值给never类型
- any也不可以赋值给never
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {throw new Error(message);
}// 推断的返回值类型为never
function fail() {return error("Something failed");
}// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {while (true) {}
}
12.联合类型
- 表示取值可以为多种类型中的一种
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
-
联合类型使用 | 分隔每个类型
-
let myFavoriteNumber: string | number; 的含义是允许 myFavoriteNumber 是string类型或者number类型,但是不能是其他类型
-
当TypeScript不确定一个联合类型的变量到底是哪个类型的时候,只能访问此联合类型中所有属性里共有的属性和方法
11.类型断言
- 类型断言就相当其他语言中的类型转换(显示转换)
- 但是不进行特殊的数据检查和解构
- 没有运行时的影响,只是在编译阶段起作用
两种形式:
- 尖括号语法
let someValue: any = 'this is a string'
let strLength: number = (<string>someValue).length
- as 语法
let someValue: any = 'this is a string'
let strLength: number = (someValue as string).length
12.类型推论
下面代码虽然没有指定类型,但是在编译的时候会报错,TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
-
如果在定义的时候没有赋值,不管之后有没有赋值,都会被推断为any类型而不被类型检查
三.变量声明
① var和let声明与JavaScript中一致
- var 声明可以在包含它的函数,模块,命名空间或者全局作用域内部任何位置被访问
- let 支持块作用域,在声明之前读或者写
- var可以多次声明,只会取最后一个
- let如果多次声明就会报错
- 使用let替换var
② const 声明
- 赋值后不能被改变
- 和let的作用域规则相同,但是不能对它们重新赋值
- 但是可以修改对象内部的值
③ let 和 const 的选择
- 使用最小特权原则,所有变量除了你计划去修改的都应该使用const
- 如果需要修改就使用let
④ 解构
解构数组
- 最简单的解构数组
let input = [1, 2]
let [first, second] = input
console.log(first)
console.log(second)
-
利用解构交换两个变量的值
let input = [1, 2]
let [first, second] = input
;[first, second] = [second, first]console.log(first)
console.log(second)
- 解构用在函数参数上
let input = [1, 2]
function f([first, second]: number[]) {console.log(first)console.log(second)
}
f(input)
-
使用剩余参数解构
let [first, ...rest] = [1, 2, 3, 4]
console.log(first)
console.log(rest)
- 只解构其中一个数据
let [first] = [1, 2, 3, 4]
console.log(first)
-
将不关心的元素省略
let [, second, , fourth] = [1, 2, 3, 4]
console.log(second)
解构对象
let o = {a: 'foo',b: 12,c: 'bar',
}
let { a, b } = o
console.log(a)
console.log(b)
-
属性重命名
let o = {a: 'foo',b: 12,c: 'bar',
}
let { a: name1, b: name2 } = o
console.log(name1)
console.log(name2)
-
默认值
-
参数默认值(如果函数没有传参数,就使用初始化列表中的值)
-
解构表达还是要尽量保持小而简单
⑤ 展开运算符
- 允许将一个数组展开为另一个数组
let first = [1, 2]
let second = [3, 4]
let bothPlus = [0, ...first, ...second, 5]
console.log(bothPlus) // [0, 1, 2, 3, 4, 5]
- 将一个对象展开为另一个对象
let defaults = { food: 'spicy', price: '$$', ambiance: 'noisy' }
let bothPlus = { ...defaults, food: 'rich' }
console.log(bothPlus) //
四.接口
- 在TypeScript中,使用接口(Interfaces)来定义对象的类型
- 在TypeScript中,除了可用于对类的一部分行为进行抽象以外,也常用于对【对象的形状】进行描述
- TypeScript核心原则:对值所具有的结构进行类型检查
- 接口的作用是为这些类型命名和为你的代码或第三方代码定义契约
- 下面的例子:定义了一个接口,接着定义了一个变量tom,类型是Person, 约束tom的形状必须和接口 Person 一致
- 定义的变量比接口少了一些属性或者多了一些属性都不被允许,必须和接口的形状保持一致
-
可选属性
① 有时候希望不要完全匹配一个形状,可以用可选属性
② 可选属性的含义就是该属性可以不存在
③ 但是仍然不允许添加未定义的属性
- 任意属性
① 有时候我们希望一个接口允许有任意的属性,上面是使用 [propName: string]
定义了任意属性取string 类型的值
② 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
③ 所以下面的案例中,如果任意属性中的值允许是string, 但是可选属性age的值是number,不是string的子属性,所以报错了
④ 一个接口中只能定义一个任意属性,如果接口中有多个类型的属性,则可以在任意属性中使用联合类型
- 只读属性
① 有时候我们希望对象中的一些字段只能在创建的时候被赋值,就使用 readonly 定义只读属性
② 只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
③ 只读属性不能进行赋值(无法修改)
interface Point {readonly x: numberreadonly y: number
}let p1: Point = { x: 10, y: 10 }
p1.x = 10 // 会报错,属性是只读属性
③ 只读属性在第一次初始化对象的时候,必须进行初始化
-
数组只读属性
let a: number[] = [1, 2, 3, 4]
// 限制数组是只读类型
let ro: ReadonlyArray<number> = aro[0] = 1 // 报错
-
readonly 和 const
① 作为变量使用的话就用const
② 作为属性就使用 readonly
- 额外的属性检查
解决方法 ①
解决方法 ② :添加一个字符串索引签名
解决方法③ 将这个对象赋值给另一个变量,不会经过额外属性检查
五.数组类型
①【类型 + 方括号】表示法
let fibonacci: number[] = [1, 1, 2, 3, 5];
② 数组的项中不允许出现其他类型
③ 数组中的一些方法的参数也会根据数组在定义时约定的类型进行限制
④ 可以使用数组泛型 Array<elemType> 来表示数组
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
⑤ 用接口表示数组
interface NumberArr {[index: number]: number
}
let fib: NumberArr = [1, 2, 3, 4]
用接口表示数组比较少见,但是经常用它表示类数组(伪数组)
使用普通数组不能定义为数组,所以使用接口的形式
常用的类数组都有自己的接口定义,比如 IArguments
, NodeList
, HTMLCollection
function sum() {let args: IArguments = arguments
}
IArgument 是TypeScript定义好的类型,实际上就是
interface IArguments {[index: number]: any;length: number;callee: Function;
}
⑥ any在数组中的应用:any表示数组中允许出现任意类型
let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];
六.函数的类型
① 函数声明:
- 输入多余或者少于的参数,是不被允许的
function sum(x: number, y: number): number {return x + y;
}
② 函数表达式
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {return x + y;
};
TypeScript 中的 => 和 ES6中的箭头不一样,TypeScript中,=>用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型
③ 用接口定义函数的形状
interface SearchFunc {(source: string, subString: string): boolean
}let mySearch: SearchFunc
mySearch = function (source: string, subString: string) {return source.search(subString) !== -1
}
-
用函数表达式或者接口定义函数时,对等号左侧进行类型限制,可以保证对函数名赋值时保证参数的个数,参数类型和返回值类型不变
④ 可选参数
- 在参数名后面加问号
- 可选参数后面不允许再出现必选参数
⑤ 参数默认值
- 允许给函数添加默认值,TypeScript会将添加了默认值的参数识别为可选参数
- 不受【可选参数必须在必选参数后面】限制了
⑥ 剩余参数
- ...rest的方式获取函数中的剩余参数
- rest只能是最后一个参数
function push(array: any[], ...items: any[]) {items.forEach(function (item) {array.push(item)})
}let a: any[] = []
push(a, 1, 2, 3)
⑦ 重载
- 重载允许一个函数接受不同数量或类型的参数时,作出不同的处理
- 注意:多个函数定义如果有包含关系,需要优先把精确的定义写在前面,因为TypeScript会优先从最前面的函数定义开始匹配
function reverse(x: number): number
function reverse(x: string): string
function reverse(x: number | string): number | string | void {if (typeof x === 'number') {return Number(x.toString().split('').reverse().join(''))} else if (typeof x === 'string') {return x.split('').reverse().join('')}
}
七.类型断言
① 概念
-
类型断言含义:可以手动指定一个值类型
-
语法:值 as 类型 <类型>值
-
在 tsx(React 的 jsx 语法的 ts 版) 语法中必须使用前者
-
<类型>这种语法在ts中除了表示类型断言之外,也有可能是表示一个泛型
-
所以在使用断言的时候,统一使用 值 as 这样的语法
② 类型断言的用途
(1) 将一个联合类型断言为其中一个类型
- 此时可以使用类型断言,将 animal 断言成 Fish
- 使用断言的时候一定要格外小心,尽量避免断言后调用方法或者引用深层属性,减少不必要的运行时的错误
(2)将一个父类断言为更加具体的子类
(3)将任何一个类型断言为any
- 在any类型的变量上,访问任何属性都是允许的
- 但是它可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用as any
- 不能滥用 as any, 也不要完全否定它的作用,需要在类型的严格性和开发的便利性之间掌握平衡
(4)将as断言为一个具体的类型
- 明确tom的类型之后,提高代码的可维护性
总结
- 联合类型可以被断言为其中一个类型
- 父类可以被断言为子类
- 任何类型都可以被断言为any
- any可以被断言为任何类型
③ 类型断言的限制
- 并不是任何一个类型都可以被断言为任何另一个类型
- 如果A兼容B,那么A能够被断言成B, B也能被断言成A
interface Animal {name: string
}
interface Cat {name: stringrun(): void
}function testAnimal(animal: Animal) {return animal as Cat
}
function testCat(cat: Cat) {return cat as Animal
}
- 允许animal as Cat 是因为 [父类可以被断言为子类]
- 允许cat as Animal 是因为既然子类拥有父类的属性和方法,那么被断言为父类,获取父类的方法,就不会有任何问题,所以子类可以被断言为父类
- 要使A能够被断言成B, 只需要A兼容B或者B兼容A即可
综上所述:
- 联合类型可以被断言为其中一个类型
- 父类可以被断言为子类
- 任何类型都可以被被断言为any
- any可以被断言为任何类型
- 要使得
A
能够被断言为B
,只需要A
兼容B
或B
兼容A
即可
④ 双重断言
interface Cat {run(): void
}
interface Fish {swim(): void
}function testCat(cat: Cat) {return cat as any as Fish
}
- 会导致运行时错误,除非迫不得已,千万别使用双重断言
⑤ 类型断言和类型转换
- 类型断言只会影响到TypeScript编译时的类型,类型断言语句在编译结果中会被删除
- 类型断言不是类型转换,它不会真的影响到变量的类型
- 如果要进行类型转换,需要直接调用类型转换的方法
⑥ 类型断言和类型声明
类型断言
- animal断言为Cat,只需要满足
Animal
兼容Cat
或Cat
兼容Animal
即可 - animal赋值为Cat, 需要满足Cat兼容Animal才行,但是Cat并不兼容Animal
- 类型声明比类型断言更加严格,最好优先使用类型声明
⑦ 类型断言和泛型
- 更加规范的实现对 getCacheData 返回值的约束
- 同时去掉了代码中的any,是最优的一个解决方案
function getCacheData<T>(key: string): T {return (window as any).cache[key]
}interface Cat {name: stringrun(): void
}const tom = getCacheData<Cat>('tom')
tom.run()
八. 声明文件
① 当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全,接口提示等功能
② 声明语句
③ 声明文件:把声明语句放到一个单独的文件中
文件以 .d.ts 为结尾 ,其他所有*.ts文件就可以获得 jQuery 的类型定义了
declare var jQuery: (selector: string) => any;
④ 第三方声明文件
- 使用 @type 统一管理第三方库的声明文件
- 直接使用npm 安装对应的声明模块
npm install @types/jquery --save-dev
⑤ 书写声明文件
库的使用场景:
- 全局变量:通过<script>标签引入第三方库,注入全局变量
- npm: 通过 import foo from 'foo' 导入,符合ES6模块规范
- UMD 库:既可以通过
<script>
标签引入,又可以通过import
导入 - 直接扩展全局变量:通过
<script>
标签引入后,改变一个全局变量的结构 - 在 npm 包或 UMD 库中扩展全局变量:引用 npm 包或 UMD 库后,改变一个全局变量的结构
- 模块插件:通过
<script>
或import
导入后,改变另一个模块的结构
(1)全局变量
declare var/let/const
- 定义全局变量
- 使用 declare var 和 declare let 没有区别
- 使用 declare const 时,表示此时的全局变量是一个常量,不允许再修改它的值了
- 一般来说,全局变量都是禁止修改的常量,所以大部分情况都应该使用const而不是var 或者 let
- 声明语句中只能定义类型,不要定义具体的实现
declare function
- 定义全局函数
-
declare function jQuery(selector: string): any;
-
函数类型的声明语句中,函数重载也是支持的
declare function jQuery(selector: string): any;
declare function jQuery(domReadyCallback: () => any): any;
declare class
- 定义的全局变量是一个类
-
declare class Animal {name: string;constructor(name: string);sayHi(): string; }
- 只能用来定义类型,不能用来定义具体的实现
declare enum
- 定义枚举类型,也被称为外部枚举
declare enum Directions {Up,Down,Left,Right
}
-
declare enum 仅用来定义类型,而不是具体的值
declare namespace
用来表示全局变量是一个对象,包含很多子属性
declare namespace jQuery {function ajax(url: string, settings?: any): void;const version: number;class Event {blur(eventType: EventType): void}enum EventType {CustomClick}
}
jQuery.ajax('/api/get_something');
嵌套的命名空间
- 如果对象拥有深层的层级,需要使用嵌套的 namespace 来声明深层的属性的类型
declare namespace jQuery {function ajax(url: string, settings?: any): void;namespace fn {function extend(object: any): void;}
}jQuery.ajax('/api/get_something');
jQuery.fn.extend({check: function() {return this.each(function() {this.checked = true;});}
});
-
如果 jQuery 下仅有fn这一个属性,可以不需要嵌套 namespace
declare namespace jQuery.fn {function extend(object: any): void;
}jQuery.fn.extend({check: function() {return this.each(function() {this.checked = true;});}
});
interface
和 type
- 声明一个全局的接口或者类型
-
interface AjaxSettings {method?: 'GET' | 'POST'data?: any; } declare namespace jQuery {function ajax(url: string, settings?: AjaxSettings): void; }// src/index.tslet settings: AjaxSettings = {method: 'POST',data: {name: 'foo'} }; jQuery.ajax('/api/post_something', settings);
-
type 与 interface类似
防止命名冲突
- 暴露在外层
interface
或type 会作为全局类型作用于整个项目中,应该尽可能减少全局变量或全局类型的数量
- 所以将他们最好放到 namespace 下
declare namespace jQuery {interface AjaxSettings {method?: 'GET' | 'POST'data?: any;}function ajax(url: string, settings?: AjaxSettings): void;
}
-
在使用interface的时候,也应该加上 jQuery 前缀
let settings: jQuery.AjaxSettings = {method: 'POST',data: {name: 'foo'}
};
jQuery.ajax('/api/post_something', settings);
声明合并
- 假如 jQuery 既是一个函数,可以直接被调用
jQuery('#foo'),又是一个对象,
拥有子属性jQuery.ajax()
(事实确实如此),那么可以组合多个声明语句,他们会不冲突的合并起来
declare function jQuery(selector: string): any;
declare namespace jQuery {function ajax(url: string, settings?: any): void;
}jQuery('#foo');
jQuery.ajax('/api/get_something');
九.内置对象
① ECMAScript 标准提供的内置对象:Boolean
、Error
、Date
、RegExp,可以在TypeScript中定义以下类型
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
② DOM和BOM的内置对象
Document
、HTMLElement
、Event
、NodeList,TypeScript中会经常用到这些类型
③ TypeScript核心库的定义文件
- 定义了所有浏览器环境需要用到的类型,并且是预置在
TypeScript中
当使用一些常用的方法的时候,TypeScript实际上已经帮你做了很多类型判断的工作
- TypeScript 核心库的定义中不包含 Node.js 部分
- 如果想用TypeScript写Node.js,需要引入第三方声明文件
npm install @types/node --save-dev
十.类型别名
- 就是给类型起一个别的名字
- 使用type创建类型别名
- 类型别名常用于联合类型
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {if (typeof n === 'string') {return n;} else {return n();}
}
十一.字符串字面量类型
- 用来约束取值只能是某几个字符串中的一个
- 使用type定了一个 字符串字面量类型
EventNames, 只能取三种字符串中的一种
类型别名和字符串字面量类型都是type进行定义的
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {// do something
}handleEvent(document.getElementById('hello'), 'scroll'); // 没问题
handleEvent(document.getElementById('world'), 'dblclick'); // 报错,event 不能为 'dblclick'
十二.元组
- 数组合并了相同类型的对象,元组合并了不同类型的对象
let tom: [string, number] = ['Tom', 25];
let tom: [string, number];
tom[0] = 'Tom';
tom[1] = 25;tom[0].slice(1);
tom[1].toFixed(2);
-
也可以赋值其中一项
let tom: [string, number];
tom[0] = 'Tom';
- 直接对元组类型变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项
-
越界的元素
① 当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型
十三.枚举
- 枚举类型用于取值被限定在一定范围内的场景
- 使用enum关键字来定义
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
-
手动赋值
① 未手动赋值的枚举项会接着上一个枚举递增
② 如果未手动赋值的枚举项与手动赋值的重复了,TypeScript是不会报错的
③ 所以使用的时候要注意,最好不要有这种情况
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};
④ 手动赋值的枚举项可以不是数字,这个时候需要使用类型断言来让tsc无视类型检查
enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = <any>"S"};
⑤ 手动赋值的枚举项也可以是小数或者负数,未赋值的项逐步增长1
- 常数项和计算所得项
① 枚举项有两种类型,常数项和计算所得项
常数项:上面的例子就是常数项
计算所得项:Blue = "blue".length
enum Color {Red, Green, Blue = "blue".length};
② 但是如果紧接在计算所得项后面的是未手动赋值的项,就会因为无法获得初始值而报错
③ 当满足以下条件时,枚举成员被当作是常数
- 不具有初始化函数并且之前的枚举成员是常数:当前枚举成员的值等于上一个枚举成员的值 + 1,第一个枚举元素的值是0
- 枚举成员使用常数枚举表达式初始化
数字字面量,引用之前定义的常数枚举成员, 带括号的常数枚举表达式, +
, -
, ~
一元运算符应用于常数枚举表达式; +
, -
, *
, /
, %
, <<
, >>
, >>>
, &
, |
, ^
二元运算符,常数枚举表达式做为其一个操作对象
所有其他情况的枚举成员被当作是需要计算得出的值
④ 常数枚举
- 使用 const enum 定义的枚举类型
const enum Directions {Up,Down,Left,Right
}let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
-
与普通枚举的区别是,会在编译阶段被删除,并且不能包含计算成员
⑤ 外部枚举
- 外部枚举是使用 declare enum 定义的枚举类型
declare enum Directions {Up,Down,Left,Right
}let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
-
declare和const可以同时使用
declare const enum Directions {Up,Down,Left,Right
}let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];