一、枚举
认识枚举
- 在很多计算机语言中都有枚举的概念,但是 JS 中是没有枚举这个概念的,为了弥补这个缺憾 在 TS 加入了枚举类型
- 什么是枚举呢 ?
- 枚举( mei ju ) : 枚举的意思就是一一列举,把所有情况都列举出来,那么取值的时候,只有这几个可以使用,其他的都不行
- 计算机语言里面的枚举( enumerations ) : 把所有的常量放在一个集合内,让若干个常量变成一组有关联的内容
// 针对一个业务逻辑,我需要频繁用到四个方向的字符串
const UP = 'up'
const RIGHT = 'right'
const DOWN = 'down'
const LEFT = 'left'
- 对于以上四个变量来说
- 我不管做任何逻辑,我没办法限制你只能使用这四个变量中的一个
// 封装一个功能函数
function util(dir) {}
- 不管用什么方法,你都没办法限制这个 dir 参数接收到的必须是上面列出的四个方向
- 这个时候,我们就可以用到枚举了
- 首先,在 TS 中,利用 enum 关键字创建一个枚举集合,把我们需要的四个常量放进去
enum Direction {
UP = 'up',
RIGHT = 'right',
DOWN = 'down',
LEFT = 'left'
}
- 制作了一个 DIrection 枚举集合,那么就可以用这个集合来对某些数据进行限制了
function util(dir: Direction) {}
- 这就约定了,dir 这个参数的值只能是 Direction 这个枚举集合里面的常量,其他都不行
- 只要你写的不是 Direction 这个枚举内的内容都不行
数字枚举
- 数字枚举 : 枚举类型中的每一个常量都是数字
- 在 TS 中,枚举内的每一个常量,当你不设置值的时候,默认就是 number 类型
enum Pages {
ONE, // 0
TWO, // 1
THREE // 2
}
- 你在枚举内的常量,第一个默认值是 0,后面的依次 +1 递
- 此时
- Pages.ONE => 0
- Pages.TWO => 1
- Pages.THREE => 2
- 我们也可以自己指定值
enum Pages {
ONE = 10, // 10
TWO = 20, // 20
THREE = 30 // 30
}
- 这个时候枚举集合内的常量就是我们指定好的值
- 我们也可以指定部分值
enum Pages {
ONE = 10, // 10
TWO, // 11
THREE // 12
}
- 指定常量后面的未指定常量,就会按照 +1 的规则一次递增
enum Pages {
ONE, // 0
TWO = 10, // 10
THREE // 11
}
enum Pages {
ONE, // 0
TWO = 10, // 10
THREE, // 11
FOUR = 30, // 30
FIVE // 31
}
字符串枚举
- 字符串枚举:枚举集合中的每一个常量的值都是 string 类型
- 在 TS 内,你必须要指定一个值,才可能会出现 string 类型
enum Direction {
UP = 'up',
RIGHT = 'right',
DOWN = 'down',
LEFT = 'left'
}
- 在 TS 中,枚举常量和任何内容都是不一样的,包括原始字符串
function util(dir: Direction) {}
- 这是因为,在 TS 中,枚举内的每一个常量都是一个独一无二的值
- 所以当你用枚举去限定一个数据的时候,用的时候也只能用枚举内的值
- 这样也避免你因为手误出现的单词错误,比如你会不会认为 ‘form’ 和 ‘from’ 是一个单词呢
异构枚举
- 异构枚举 : 其实就是在一个枚举集合内同时混合了数字枚举和字符串枚举
- 但是你大概率是不会这样使用的,因为我们作为一组数据的集合,一般不会把数字和字符串混合在一起使用
enum Info {
ONE,
UP = 'up',
TWO = 2,
LEFT = 'left'
}
- 在这里有一个点需要注意
- 因为在枚举集合内,当某一个 key 你没有设置值的时候,会默认按照上一个的值 +1
- 所以如果前一个是 字符串枚举,那么下一个必须要手动赋值,不然会报错
- 如果前一个是 数字枚举,那么下一个可以不必要手动赋值,会按照上一个 +1 计算
枚举合并
- 在 TS 内的枚举,是支持合并的
- 多个枚举类型可以分开书写,会在编译的时候自动合并
enum Direction {
UP = 'up',
RIGHT = 'right',
DOWN = 'down',
LEFT = 'left'
}
enum Direction {
TOP = 'top',
BOTTOM = 'bottom'
}
function util(dir: Direction) {}
util(Direction.BOTTOM)
util(Direction.LEFT)
- 这里定义的两个枚举都叫做 Direction,会在编译的时候自动放在一起,不会出现冲突
反向映射
- TS 内的数字枚举,在编译的时候,会同时将 key 和 value 分别颠倒编译一次
enum Pages {
ONE, // 0
TWO, // 1
THREE // 2
}
- 以这个为例,他是如何进行编译的呢
var Pages;
(function (Pages) {
Pages[Enum["ONE"] = 0] = "ONE"
Pages[Enum["TWO"] = 1] = "TWO"
Pages[Enum["THREE"] = 2] = "THREE"
})(Pages || (Pages = {}));
- 编译完毕的结果
Pages = {
ONE: 0,
TWO: 1,
THREE: 2,
'0': 'ONE',
'1': 'TWO',
'2': 'THREE'
}
- 也就是说,我们在 TS 内使用的时候,如果是数字枚举
- 那么我们可以通过 key 得到对应的数字,也可以通过对应的数字得到对应的 key
enum Pages {
ONE, // 0
TWO, // 1
THREE // 2
}
console.log(Pages.ONE) // 0
console.log(Pages.TWO) // 1
console.log(Pages.THREE) // 2
console.log(Pages[0]) // 'ONE'
console.log(Pages[1]) // 'TWO'
console.log(Pages[2]) // 'THREE'
常量枚举
- 常量枚举,是在枚举的基础上再加上 const 关键字来修饰
- 会在编译的时候,把枚举内容删除,只保留编译结果
- 并且对于数字枚举来说,不在支持反向映射能力,只能利用 key 来访问
- 非常量枚举
enum Pages {
ONE, // 0
TWO, // 1
THREE // 2
}
console.log(Pages.ONE)
console.log(Pages.TWO)
console.log(Pages.THREE)
- 编译完毕的 js 文件
- 常量枚举
const enum Pages {
ONE, // 0
TWO, // 1
THREE // 2
}
console.log(Pages.ONE)
console.log(Pages.TWO)
console.log(Pages.THREE)
- 编译完毕的 js 文件
二、类型约束
- 在 TS 中,还有一个很神奇的关键字,叫做 type
- type 又叫做类型别名有很多神奇的功能,不仅能支持 interface 定义的对象结构,还支持任何手写类型
- 先来看一个很简单的例子
let n1: number | string | boolean
let n2: number | string | boolean
let n3: number | string | boolean
- 观察上面一段代码,我们定义了 n1 和 n2 和 n3 三个变量
- 对于类型的限制都是 number 或者 string 或者 boolean
- 写起来的时候就非常麻烦
- 这个时候,我们就可以使用 type 对其进行别名设置
type Info = number | string | boolean
let n1: Info
let n2: Info
let n3: Info
- 这样一来,我们的代码是不是变得简洁了起来
- 可能小伙伴们认为这个用的并不多,但是 type 也不是只有这一个功能
type 的常见使用
- 基本类型的别名
type n = number
let num: n = 100
- 这是一个非常基础的使用,把 number 这个类型起了一个别名叫做 n
- 今后再用 n 来限制变量的时候,其实就是在使用 number
- 基本类型联合
type i = number | string
let str: i = '千锋大前端'
str = 100
- 这就是联合类型,那 number 或者 string 这个类型齐了一个别名叫做 i
- 我们再用 i 来限制变量的时候,这个变量就被限制为了 number 或者 string
- 对象类型
type User = { name: string, age: number }
let person: User = { name: '千锋大前端', age: 10 }
- 这就是对象类型,和 interface 很像,用处基本一致
- 对象联合类型
type User = { name: string, age: number }
type Person = User & { gender: boolean }
let person: Person = { name: '千锋大前端', age: 10, gender: true }
- 这就是对象联合类型,和 interface 的 extends 继承很像
- 元组类型
type data = [ number, string ]
let info: data = [ 10, '千锋大前端' ]
- 函数类型
type func = (x: number, y: number) => number
- 常量限定
type color = 'yellow' | 'orange' | 'blue'
function util(c: color) {}
util('yellow')
- 这个 color 被限定为了几个值,将来用 color 去约束一个变量的时候
- 这个变量只能接受这几个值,这里和 enum 比较像了
type 和 interface 的共同点
- 都可以约束 对象 或者 函数 类型
- interface
interface User { name: string; age: number }
interface Func { (x: number): number }
- type
type User = { name: string; age: number }
type Func = (x: number) => number
- 我们看到,两个定义方式略有区别,但是后期用法基本一致
- 扩展类型
- interface 使用 extends 进行继承
interface Person {
name: string
age: number
}
// 使用 extends 关键字继承自 Person
interface Student extends Person {
classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }
- type 使用 交叉(&) 来实现
type Person = {
name: string
age: number
}
// 使用 交叉(&)
type Student = Person & {
classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }
- 联合类型
- interface 使用 extends 继承 type
type Person = {
name: string
age: number
}
// 使用 extends 关键字继承自 Person
interface Student extends Person {
classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }
- type 使用 交叉(&) 扩展 interface
interface Person {
name: string
age: number
}
// 使用 交叉(&)
type Student = Person & {
classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }
type 和 interface 的区别
- interface 支持多次声明自动合并,type 不支持
interface User {
name: string
age: number
}
interface User {
classRoom: string
}
/*
真实的 User 接口
{
name: string
age: number
classRoom: string
}
*/
- type 如果声明重名标识符会报错
- 对于 ES6 模块化语法的默认导出语法
- interface 支持声明的同时进行默认导出
export default interface User {
name: string
age: number
}
- type 必须先声明,在默认导出
type User = {
name: string
age: number
}
export default User
- 必须要先声明好,在进行默认导出,如果直接连写默认导出,会报错
- type 可以使用 typeof 关键字去获取某一数据类型
let box = document.querySelector('.box')
type EleType = typeof box
- 这里定义了一个 EleType 标识符,会自动根据 typeof 关键字检测的 box 的类型限制
- type 支持使用 in 关键字去遍历成映射类型
type names = 'firstName' | 'lastName' | 'AKA'
type nameType = {
[key in names]: string
}
/*
真实的 nameType 类型
{
firstName: string
lastName: string
AKA: string
}
*/