赞
踩
1、阅读本文章,需要具备的知识技能:
- JavaScript
- node.js基础
2、阅读本文章,需要安装的环境和工具:
- VSCode编辑器(推荐,其它编辑器也可)
- Node环境(可通过此链接进行安装:https://nodejs.org/zh-cn/download/)
TypeScript是JavaScript类型的超集,它可以编译成JavaScript代码。
TypeScript可以在任何浏览器、任何计算机和任何操作系统上运行,并且是开源的。
TypeScript是JavaScript类型的超集的意思是,TypeScript在ES6基础上做了升级,而ES6是基于ES5进行的升级,这是TypeScript是JavaScript超级的理解。
TypeScript属于静态类型的编程语言,JS属于动态类型的编程语言。
TS在代码编译的时候(代码执行前)就可以发现错误(早)
JS在代码执行的时候(编译后执行时)才能发现错误(晚)
1.更早(写代码的同时)发现错误,减少找Bug、改Bug时间,提升开发效率。
2.程序中任何位置的代码都有代码提示,随时随地的安全感,增强了开发体验。
3.强大的类型系统提升了代码的可维护性,使得重构代码更加容易。
4.支持最新的 ECMAScript语法,优先体验最新的语法,让你走在前端技术的最前沿。
5.TS类型推断机制,不需要在代码中的每个地方都显示标注类型,让你在享受优势的同时,尽量降低了成本。
typescript
包来编译.ts
文件安装命令: npm i -g typescript
。
typescript
包:用来编译TS代码的包,提供了tsc
命令,实现了TS-> JS
的转化。
验证是否安装成功:tsc-V
(查看 typescript的版本)。
使用方法:
tsc code.ts
(此时在同级目录中会出现一个同名的code.js
文件)node code.js
ts-node
包来编译.ts
文件上面的执行命令比较繁琐,提供一个比较简单的命令
**安装命令: **npm i -g ts-node
ts-node
包在内部已经将TS
转化 JS
,然后再运行JS
代码
使用方法:ts-node 文件名称
,例如:ts-node code.ts
esno
包来编译.ts
文件上面的执行命令比较繁琐,提供一个比较简单的命令
**安装命令: **npm i -g esno
esno
内部使用了esbuild 来加载 TypeScript 和 ESM的Node.js运行时
使用方法:esno 文件名称
,例如:esno code.ts
ts-node-dev
包来编译.ts
文件【推荐】上面通过
esno
或ts-node
执行命令,如果更改了代码,esno
或ts-node
不支持自动监听ts文件变化,自动重新执行程序,而ts-node-dv
包就可以实现自动监听文件的变化然后自动重新执行最新的程序,节省了重新启动命令的时间,更加方便。
**安装命令: **npm i -g ts-node-dev
运行ts-node-dev命令,还有一个简短的别名命令tsnd
,可以在终端中输入 tsnd
检查下是否安装成功。如果出现如下图所示内容,说明安装成功了。
使用方法:tsnd --respawn 文件名称
,例如:tsnd --respawn code.ts
,在文件中,更改代码后,会自动执行更改后的代码运行结果。
JS有类型,但是JS不会检查变量的类型是否发生变化,而TS会检查
TypeScript支持与JavaScript几乎相同的数据类型。
以下TS代码,放到一个ts文件中,并通过
tsnd --respawn 文件名称
,例如:tsnd --respawn code.ts
方式进行执行。
// : string 为类型注解,表示变量name1为字符串类型。
// 在TS中,一旦为变量添加了类型注解,这个变量的类型就不能发生改变,否则编译就会报错。
let name1: string = 'xiaozhu'; // 字符串类型
let name2: string = `xiaozhu`; // 模版字符串
let age: number = 18; // 数值类型
let cool: boolean = true; // 布尔值
let u: undefined = undefined; // Undefined
let n: null = null; // Null
// TypeScript像JavaScript一样可以操作数组元素。有两种方式可以定义数组。
// 第一种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组:
let arr1: number[] = [1, 2, 3]; // 数组
// 第二种方式是使用数组泛型,Array<元素类型>:
let list: Array<number> = [1, 2, 3];
// 有小括号,表示首先是数组,然后数组中可以出现number和string类型的元素
let arr2: (number | string)[] = [1,3,'a','c']
let arr3: (number | string)[] = [1,3]
let arr4: (number | string)[] = ['a','b']
// 没有小括号,既可以是number 又可以是 string数组(数组中的元素必须是string类型)
let arr5: number | string[] = ['a','b','3']
let arr6: number | string[] = 123
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
let array7: number[] = [1,2,3,5]; // 有局限性
// 使用元组,当能够确切的知道包含都少个元素,以及特定索引对应的类型时,需要使用元组
let position1: [ number,number ] = [12, 13] // OK,数组中第一个和第二个都是数值
let position2: [ number,string ] = [12, '13'] // OK ,数组中第一个是数值、第二个字符串
let position3: [ number,string ] = ['12', 12] // Error,跟元组类型对应类型不一致
let position4: [ number,string ] = [12,'13',14] // Error,跟元组类型对应类型以及元素个数不一致。
但使用元组确实能帮助我们进一步提升数组结构的严谨性。
any
指的是一个任意类型,如果我们想要为那些在编程时不太清楚的类型变量指定一个类型时,可以使用any类型来标记这些变量。但是也是由于any
提供了这样的“便利性”,我们可以对任何为any
的类型的变量进行任何操作,即使是在获取该类型的变量中没有的属性或者方法,也不会报任何错误,如下代码所示:
let everybody: any = []
everybody.name = '筱竹' // 此处不会提示报错
everybody.age = 18 // 此处不会提示报错
everybody.sayHi() // 此处不会提示报错
let age: number = everybody // 此处不会提示报错
let name: string = everybody // 此处不会提示报错
根据上述的"错误演示",给变量设置any
类型不推荐使用(不推荐,不建议使用any
类型),因为如果在TS代码中很多变量都是any
类型,此时TS的静态类型的检查(静态类型:编译期做类型检查)不起任何的作用(any
会绕过TS的类型检查),跟没有用TS是一样的,所以原则上不推荐使用any
类型。
void
指的是没有任何类型,某种程度上来说,void
类型像是与any
类型相反, void
用于描述一个内部没有 return
语句,或者没有显式 return
一个值的函数的返回值,我们可以认为 void 表示一个空类型,如:
// 如果函数没有返回值,那么函数的返回值类型 为 void(空)
function handle(name: string): void {
console.log('您好',name)
}
handle('张三') // 您好 张三
never
指的是永远不存在的值的类型, 例如, never
类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型;
// error函数的类型因为是never,永远不会有返回值,所以它的返回值类型就是 never。
function error(msg: string): never {
throw new Error(msg) // [ERROR] Error: 张三
}
error('张三')
// infiniteLoop函数体中的代码是一个死循环,那么这个函数的返回值类型也是 never
function infiniteLoop(): never {
while (true) {}
}
never
类型可以是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never
的子类型或可以赋值给never
类型(除了never
本身之外)。 即使 any
也不可以赋值给never
,如下代码所示:
// 没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never,
let MyAge: never = 18 // 提示:不能将类型“number”分配给类型“never”。
let MyName: never = 'string' // 提示:不能将类型“string”分配给类型“never”。
let MyCool: never = true // 提示:不能将类型“boolean”分配给类型“never”。
// never类型是任何类型的子类型,也可以赋值给任何类型,如下所示:
let Age: number = MyAge // never类型可以赋值给 number类型
let Name: string = MyName // never类型可以赋值给任何类型
let Cool: boolean = MyCool // never类型可以赋值给任何类型
unknown
是 TypeScript 3.0 版本中新添加的一个顶级类型,用来描述类型并不确定的变量, 对照于any
,unknown
是类型安全的(因为any
会绕过TS类型检查,而unknown
会进行类型的检查,所以unknown
是类型安全的,当我们不知道是什么类型的时候,可以用unknown
替代any
), 任何值都可以赋给unknown
,但 unknown
类型的值只能赋值给 unknown
或 any
。
let userName: unknown = '筱竹'
let userAge: unknown = 18
let userBool: unknown = true
let info: unknown
let typeUnknown: unknown = info // unknown 类型的值只可以赋值给 unknown 或 any
let everything: any = info // unknown 类型的值只可以赋值给 unknown 或 any
let num: number = info // 不能将类型“unknown”分配给类型“number”。ts(2322)
函数类型包含两部分:参数类型和返回值类型。
给函数类型,单独指定参数类型和返回值类型,代码如下:
// x: number, y: number 为参数类型,():number为返回值类型
function add(x: number, y: number): number {
return x + y
}
console.log('add', add(1, 2)) // add 3
let myAdd = (x: number, y: number): number => {
return x + y
}
console.log('myAdd', myAdd(2, 3)) // myAdd 5
给函数类型,同时指定参数类型和返回值类型,代码如下:
// (x: number, y: number) => number 同时为函数类型指定了参数类型和返回值类型
// (x, y) => { return x + y } 这是函数的实现
let Add: (x: number, y: number) => number = (x, y) => {
return x + y
}
console.log('Add', Add(3, 4)) // Add 11
在TypeScript里我们可以在参数名后面使用 ?
实现可选参数的功能,可选参数就是 参数是可选的,可传可不传。
function helloName(name1: string, name2?: string) {
console.log(`hello:${name1},${name2}`)
}
helloName('筱竹', 'TS') // hello:筱竹,TS
helloName('筱竹') // hello:筱竹,undefined
function helloAge(age1?: number, age2?: number) {
console.log(`age:${age1},${age2}`)
}
helloAge(18, 3) // age:18,3
helloAge(18) // age:18,undefined
注意:必选参数不能位于可选参数后,如下所示:
// 注意必选参数不能位于可选参数后,如下所示(错误的):
function helloNum(num1?: number, num2: number) {
console.log(`hello:${num1},${num2}`)
}
helloNum(1, 2)
上面的代码把鼠标移动到num2
参数上是会提示:必选参数不能位于可选参数后,如下图所示:
在TypeScript中,我们也可以为参数提供一个默认值,假如用户没有传递这个参数或传递的值是undefined时,就会使用默认参数的值。
// 在TypeScript里,我们也可以为参数提供一个默认值,假如用户没有传递这个参数或传递的值是undefined时,就会使用默认参数的值。
function sayHi(name?: string, age = 18) {
console.log(`hi大家好,我是${name},我今年:${age}了`)
}
sayHi('筱竹') // hi大家好,我是筱竹,我今年:18了
sayHi('筱竹', 20) // hi大家好,我是筱竹,我今年:20了
const obj1: {name: string; age: number; sayHi(): void} = {
name:'张三',
age:10,
sayHllo(){
console.log('hello')
}
}
console.log(obj1.age) // 10
obj1.sayHllo() // hello
// 把属性分成多行显示时,分号可以省略
const obj2: {
name: string
age: number
sayHllo(): void
} = {
name:'张三',
age:10,
sayHllo(){
console.log('hello')
}
}
console.log(obj2.age) // 10
obj2.sayHllo() // hello
使用类型别名 可以为任意类型指定别名
使用场景:当同一个类型被重复使用多次时,可以使用类型别名来简化该类型的使用。
type typeObj = { name: string age: number sayHllo:() => void } const obj3: typeObj = { name: '张三2', age: 31, sayHllo:() =>{ console.log('hello') // hello } } obj3.sayHllo() // ------------ type typeArr = (number | string)[] let arr1: typeArr = [1, 2, '3', 4] let arr2: typeArr = ['张三', 18, '李四', 14] console.log('arr1', arr1) // arr1 [ 1, 2, '3', 4 ] console.log('arr2', arr2) // arr2 [ '张三', 18, '李四', 14 ]
因为TypeScript的核心原则之一是对值所具有的结构进行类型检查,那么接口(interface)的作用就为对象中属性的类型命名定义契约(描述对象属性值的类型),同时当一个对象类型被多次重复使用时,使用接口(interface),也能够达到复用的目的。
interface typeObj { name: string age: number sayHllo: () => void } const person1: typeObj = { name: '张三', age: 19, sayHllo: () => { console.log('hello') // hello }, } const person2 = <typeObj>{ name: '李四', age: 18, sayHllo: () => { console.log('hi') // hi }, } person1.sayHllo() person2.sayHllo() let person3 = { name: '王五', age: 20, sayHllo: () => { console.log('您好') }, } let foo = (lableObj: typeObj) => { console.log(lableObj.age) // 20 lableObj.sayHllo() // 您好 } foo(person3)
注意:
interface typeObj { name: string age: number sayHllo: () => void } let person4 = { name: '赵六', sayHllo: () => { console.log('您好') }, } let foo = (lableObj: typeObj) => { lableObj.sayHllo() } foo(person4) // 没有传入必要的age属性会报错,Error: Property 'age' is missing in type '{ name: string; sayHllo: () => void; }' but required in type 'typeObj'.
interface typeObj { name: string age: number sayHllo: () => void } let person5 = { age: 21, sayHllo: () => { console.log('您好') }, name: '赵六', } let foo = (lableObj: typeObj) => { lableObj.sayHllo() // 您好 } foo(person5)
类型别名(type)与接口(interface)区别:
**相同点:类型别名(type)和接口(interface)**都可以描述一个对象(给对象指定类型),如
type typeObj = {
name: string
age: number
sayHllo:() => void
}
interface typeObj {
name: string
age: number
sayHllo: () => void
}
不同点:接口(interface) 只能为对象指定类型,而 **类型别名(type)**不仅仅能够为对象指定类型,而且能够为任意类型指定别名。
type Name = string // 基本类型
let Name = '筱竹'
console.log('Name', Name) // Name 筱竹
type NameAge = string | number // 联合类型
let NameAge = 18
console.log('NameAge', NameAge) // NameAge 18
type typeArr = [string, number, boolean] // 元组类型
let Arr: typeArr = ['筱竹', 18, true]
console.log('Arr', Arr) // Arr [ '筱竹', 18, true ]
接口里的属性不全都是必需的,有些是只在某些条件下存在,或者根本不存在。给函数传入的参数对象中只有部分属性赋值了。带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?
符号。
// 对象中的【可选属性】(method属性可以省略),【可选属性】名字定义的后面加一个?符号, // 可选属性的2个好处: // 1.是可以对可能存在的属性进行预定义 function myAxios(config: {url: string; method?: string}){ console.log(config) } myAxios({url:'https://api.twitter.com'}) // 2.是可以捕获引用了不存在的属性时的错误 // 我们故意将 createSquare里的color属性名拼错,就会得到一个错误提示: interface GetConfig { url: string; method?: string; } function myAxios(config: GetConfig): { url: string; method?: string } { let newMethod = {url: "https://www.baidu.com", method: 'Get'}; if (config.url) { newMethod.url = config.url; } if (config.methods) { // Error: Property 'methods' does not exist on type 'GetConfig'. Did you mean 'method'? newMethod.method = config.method; } return newMethod; } let mySquare = myAxios({url: "https://www.boxuegu.com"});
能够从一个接口里复制属性或者方法到另一个接口里,可以更灵活地将接口抽离到可重用的模块里,实现复用。
// interface infObj1 { x: number; y: number } // interface infObj2 { x: number; y: number; z: number } // 以上简写方式(使用接口继承) interface infObj1 { x: number y: number } // 接口infObj2 继承了 infObj1中的属性 interface infObj2 extends infObj1 { z: number } const positionObj: infObj2 = { x: 1, y: 1, z: 3, } console.log('positionObj.x', positionObj.x) // positionObj.x 1 console.log('positionObj.z', positionObj.z) // positionObj.z 3 // 接口infObj3 继承了 infObj1中的属性 interface infObj3 extends infObj1 { z: number } // 使用接口 infObj3 const positionObj1 = <infObj3>{ x: 1, y: 1, z: 3, } console.log('positionObj1.x', positionObj1.x) // positionObj1.x 1 console.log('positionObj1.z', positionObj1.z) // positionObj1.z 3
在TS中,有些没有明确指出类型的地方,TS中的类型推论会帮助提供类型,也就是说即使有些地方不写类型注解,TS也会根据类型推论的机制来提示类型。
类型推论可以分为基础的类型推论和上下文推论
基础的类型推论如下:
鼠标放到变量名称上去后的,TS类型推论机制会帮助提示类型,如下图所示:
代码如下所示:
let userName = 'xiaozhu' // 类型推论 为 string 类型 // 等价于 let userName:string = 'xiaozhu' 类型注解:string可以省略 let userAge = 3 // 类型推论 为 number 类型 // 等价于 let userAge:number = 3 类型注解:number可以省略 let userInfo = ['筱竹', 18, true, undefined] // 类型推论为(string | number boolean | undefined)[] /** * 等价于 let userInfo:(string | number boolean | undefined)[] = ['筱竹', 18, true, undefined] * 类型注解 :(string | number boolean | undefined)[] 可以省略 * **/
上下文推论
顾名思义,所谓上下文推论,就是TS的类型推论根据变量所在的上下文环境进行推论变量的类型。如下代码所示:
// 推论出函数参数 num1 的类型是number类型,返回值的类型也是 number类型
function fn(num1 = 10, num2: number) {
return num1 + num2
}
需要注意的是,虽然TS中有类型推论的机制来帮助我们提供类型,提升开发效率,但并不代表我们在任何场景下都不需要写类型注解,而完全依赖于类型推论的帮助提供类型,而是要分清楚情况,TS的类型机制适用于如下3个场景:
1、变量初始化赋值时
let userName = 'xiaozhu' // 类型推论 为 string 类型
2、函数中有默认值的函数参数
// 推论出函数参数 num1 的类型是number类型,返回值的类型也是 number类型
function fn(num1 = 10, num2: number) {
return num1 + num2
}
3、函数中函数返回的类型
// 推论出函数返回值的类型是 number类型
function fn(num1:number, num2: number) {
return num1 + num2
}
TypeScript的类型检查不一定是最准确的,有时候,我们需要手动的指定值的类型,才能得到我们想要的类型,如下图代码所示:
以下是通过类型检查,显示 divEl
的类型可能是 HTMLElement
或者是 null
,但是我们根据代码更直观的看出,divEl
的类型就是 HTMLElement
,所以这个时候,我们比TS
更清楚一个值的类型,这时候我们就可以使用类型断言来指定更具体的类型了。
根据以上代码,使用类型断言如下所示:
const divEl = document.getElementById('div') as HTMLElement
类型断言使用 as
语法来实现,as
后面的类型是具体的类型。
以上代码,使用类型断言后,通过类型推断得出,divEl
的类型可能是 HTMLElement
,没有了 null
类型。如下图所示:
类型断言除了使用 as
语法以外,也可以使用 <>
语法,如下代码所示:
const divEl = <HTMLElement>document.getElementById('div')
由于TS中的<>
语法表示类型断言,在结合JSX
中的<>
语法,代码解析上有冲突,所以不建议使用<>
这个类型断言语法,更建议使用 as
语法,同时TS在 .tsx
文件里禁用了使用<>
语法的类型断言。
在TS中的字面量类型,跟我们在JS中的字面量类型理解上还不太一样,如下代码所示:
// 以下代码中的字面量类型是什么?
let num1 = 12 // number 类型
const num2 = 13 // 13 类型,why?
我们根据TS中的类型推论,不难看出 num2
的类型是 确实是 13
类型,不是我们直观意义上的 number
类型,为什么13
可以作为类型呢?在TS中,这就叫字面量类型(Literal Types)。
由于 let
声明的变量,变量的值是可以变化的(可以重新赋值),所以类型为 number
类型。
由于 const
声明的变量,变量的值不能变化(被赋值后,不能再改变),只能是 13
类型,所以类型为 13
类型。
在TS中字面量类型主要包括:字符串字面量类型、数字字面量类型、布尔字面量类型和对象字面量类型,它们都可以直接作为类型注解进行使用,如下代码所示:
// 字符串字面量类型 const str: 'xiaozhu' = 'xiaozhu' // 数字字面量类型 const num: 18 = 18 // 布尔字面量类型 const bool: true = true // 对象字面量类型 interface ObjInfo { name: 'xiaozhu' age: 16 } const obj: ObjInfo = { name: 'xiaozhu', age: 16, }
在TS中字面量类型相较于TS基础类型(原始类型)更加精确、严谨,因为字面量类型要求变量的值,必须跟字面量完全一致才行,不然就会报错,而TS基础类型,只要是符合基础类型中的任意同类型值就可以,就不会报错。
如下图所示:
在实际应用中,TS中的字面量类型常与联合类型、接口(或类型别名)等一起搭配使用,用来表示一组明确的可选值列表,如下代码所示:
interface Options { behavior: 'go' | 'jump' | 'run' | 'sit' sex: '男' | '女' isShow: true | false age: 20 | 30 | 40 | 50 say: (() => void) | null } const people: Options = { behavior: 'go', sex: '女', isShow: true, age: 20, say: null, } // --------------- type Direction = 'up' | 'right' | 'down' | 'left' function move(config: Direction) { console.log(config) // up } move('up')
枚举(Enum)是TS中新增的一个类型,枚举用来定义一个带名字的常量集合,枚举类型的使用类似于字面量类型+联合类型的组合使用,enum
是枚举的语法。
我们对以下的类型别名代码,改造成枚举类型。
type Direction = 'up' | 'right' | 'down' | 'left' // 改造成枚举类型, // 可以看出枚举定义了一个带名字的常量的集合,这样的好处相对于以上类型别名的代码写法更加简洁 enum Direction2 { up, right, down, left } function go(config: Direction2) { console.log(config) // 0 } // 在枚举中,通过点语法来获取枚举中的常量成员 go(Direction2.up)
当我们把鼠标移动到枚举常量成员上时,发现枚举常量成员是有值的,枚举第一个成员默认值是 0,剩下成员的值依次累加1,这也就解释了,为什么 在go
函数中,console.log(config)
的值为什是0了。
TS中的枚举主要包括:数字枚举、字符串枚举、异构枚举、常量成员和计算成员、枚举成员类型和联合枚举、常量枚举、外部枚举。
4.4.1 数字枚举
以下代码,我们定义了一个数字枚举 up的值默认从 0开始,其它枚举中的成员会从1开始递增1。
// 数字枚举
enum Direction2 {
up,
right,
down,
left
}
jump(Direction4.left)
以下代码,我们定义了一个数字枚举, up
使用初始化为 10。 其余的成员会从 10开始自动递增1。
// 数字枚举
enum Direction3 {
up = 10,
right,
down,
left,
}
function run(option: Direction3) {
console.log(option) // 11
}
以下代码,我们定义了一个数字枚举,给枚举中的成员分别初始化一个数字。
// 数字枚举
enum Direction4 {
up = 10,
right = 14,
down = 18,
left = 20,
}
function jump(info: Direction4) {
console.log(info) // 20
}
jump(Direction4.left)
4.4.2 字符串枚举
在一个字符串枚举里,枚举中每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
因为字符串枚举成员不能自增,所以需要给每个字符串枚举成员设置初始值。
// 字符串枚举(用字符串字面量初始化枚举中的成员) enum Direction5 { up = 'UP', down = 'DOWN', left = 'LEFT', right = 'RIGHT', } // 字符串枚举(用另外一个字符串枚举成员进行初始化) enum Direction6 { up = Direction5.up, down = Direction5.down, left = Direction5.left, right = Direction5.right, }
4.4.3 异构枚举
一个枚举中同时包含字符串成员和数字成员,这样的枚举就称之为异构枚举。
enum Direction7 {
name = 'xiaozhu',
age = 18,
}
4.4.4 常量成员和计算成员
以下代码中 Info1
、Info2
中枚举成员都是常量成员
enum Info1 {
num1,
num2,
num3,
}
enum Info2 {
num1 = 1,
num2,
num3,
}
当一个表达式满足下面条件之一时,它就是一个常量枚举表达式:
+
, -
, ~
其中之一应用在了常量枚举表达式+
, -
, *
, /
, %
, <<
, >>
, >>>
, &
, |
, ^
的操作对象。 若常数枚举表达式求值后为 NaN
或 Infinity
,则会在编译阶段报错。除了以上情况之外,所有其它情况的枚举成员都是计算成员。
常量成员和计算成员代码如下所示:
enum FileAccess {
// 常量成员
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
// 计算成员
G = '123'.length,
}
4.4.5 枚举成员类型和联合枚举
4.4.6 常量枚举
常量枚举通过在枚举上使用 const
修饰符来定义。
const enum Direction8 {
up = 'UP',
down = 'DOWN',
left = 'LEFT',
right = 'RIGHT',
}
常量枚举只能使用常量枚举表达式(常量枚举不允许包含计算成员),并且不同于常规的枚举,它们在编译阶段会被删除。常量枚举成员在使用的地方会被内联进来。 因此常量枚举的成员都必须是常量成员,如下代码所示:
// code.ts
const enum Direction9 {
up,
down,
left,
right,
}
let info = [Direction9.up, Direction9.down, Direction9.left, Direction9.right]
通过 tsc code.ts
命令,生成 code.js
文件。TS代码编译成 JS代码后,Direction9
枚举的定义被删除了,info
数组中对 Direction9
的引用也变成了常量值的引用(Direction9.up
内联了 0、Direction9.down
内联了 1、Direction9.left
内联了 2 、Direction9.right
内联了 3),如下代码所示:
// code.js
var info = [0 /* Direction9.up */, 1 /* Direction9.down */, 2 /* Direction9.left */, 3 /* Direction9.right */]
4.4.7 外部枚举
在 TS 中,可以通过 declare
关键字描述一个已经存在的枚举类型,通过 declare
定义出来的 枚举类型 叫 外部枚举。
declare enum UserInfo {
name = 'xiaozhu',
age = 18,
}
在TS中,其它类型仅仅被当做类型,而枚举不仅用作类型使用,还可以提供值(枚举成员都是有值的),也就说,在TS中其它的类型被当做类型时,TS代码会在编译为JS代码时,自动把类型删除掉,而枚举类型会被编译为JS代码。
// code.ts
// 字符串类型
let userName: string = 'xiaozhu'
// 枚举类型
enum Description10 {
run = 'RUN',
jump = 'JUMP',
go = 'go',
sit = 'SIT',
}
以上代码,通过 tsc code.ts
命令,会编译成如下代码:
// code.js
// 字符串类型
var userName = 'xiaozhu'; // 自动把:string 类型注解删除掉了
// 枚举类型
// 枚举类型会编译为JS代码,不会把枚举类型和类型的值删除掉
var Description10;
(function (Description10) {
Description10["run"] = "RUN";
Description10["jump"] = "JUMP";
Description10["go"] = "go";
Description10["sit"] = "SIT";
})(Description10 || (Description10 = {}));
后续TypeScript学习总结(二)高级类型等内容正在编写中,敬请期待。☀️
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。