「Typescript之旅」: 带你轻松掌握枚举🧐

701 阅读7分钟

image.png

image.png image.png

前言

通俗来说,枚举就是对一个对象的所有可能取到的值的集合, 在Typescript使用enum关键字来定义枚举。

应用场景

image.png

image.png 比如方向有上下左右; 商品的交易状态有等待买家付款、买家已付款、卖家已发货以及交易成功; 颜色有红色、蓝色等; 有星期一、星期二....星期日; 有一月到12月等等表示有限数据集的都可以使用枚举。

image.png

image.png

定义枚举

我们要使用一组相关的命名常量如星期的组合,这时候可以定义一个枚举类型:

// 定义一个枚举类型 Weekday,表示一周的工作日
enum Weekday {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
}

数字枚举

数字枚举的特点

数字枚举的特点:枚举变量所对应的值是自动递增的。

  • 我们没有初始化枚举类型的值的情况下,Monday的值是0、Tuesday的值是1、Wednesday的值是2, 而递增到Friday的值是4。

使用:

  • 如果我们对枚举进行初始化

    enum Weekday {
        Monday = 10,
        Tuesday,
       	...
    }
    

    Monday被初始化为10, 因为枚举递增的特性,Tuesday的值是11, Wednesday的值是12,递增到Friday的值是14。初始化的值也可以是负数。

使用枚举

使用枚举也很简单,像对象一样,枚举名.枚举成员。

let today: Weekday = Weekday.Wednesday;
console.log(today); // 输出: 2,因为 Wednesday 在枚举中的索引是 2

每一个枚举成员都可以当作枚举本身进行使用

function getWeekDay(day: Weekday) {
	console.log(day)
}
getWeekDay(Weekday.Friday) // 4

枚举成员使用计算值

通过上面的例子我们可以发现,每个枚举成员都有一个与之关联的值,该值可以是常量(如我们Weekday枚举类型中的Monday成员就是常量值),但也可以是计算值。计算值是的意思就是枚举的值是被计算出来的。

function getNum() {
	return 10
}
enum Weekday {
	Monday = getNum(),
	Tuesday,
	Wednesday,
	Thursday,
	Friday
}

此时Monday的值就是计算值, 但是上面的代码报错了,这是因为没有初始化的枚举要么需要放在第一个,要么必须位于使用数字常量或其他常量枚举成员初始化的数字枚举之后。这里的"其他常量枚举成员初始化的数字枚举"可能有点拗口,其实就是这样:

enum Num {
	one = 1,
	two = 2
}
enum Weekday {
	Monday = Num.two,
	Tuesday,
	Wednesday,
	Thursday,
	Friday
}

这里的Monday的值就是其他常量枚举成员初始化的数字枚举值。

字符串枚举

字符串枚举是啥

枚举不仅可以用数字常量初始化,也可以用字符串常量初始化,当一个枚举中成员的值都是字符串,那么这个枚举就是字符串枚举了。

字符串枚举与数字枚举的区别

  • 与数字枚举相比, 字符串枚举没有“递增”这个概念了。
enum Weekday {
	Monday = "Monday",
	Tuesday = "Tuesday",
	Wednesday = "Wednesday",
	Thursday = "Thursday",
	Friday = "Friday"
}

其他

其他的使用与数字枚举一样。

异构枚举

异构枚举是啥

如果枚举成员的值是数字常量和字符串常量混合的枚举类型,那么这个枚举就是异构枚举了 。

enum Weekday {
	Monday = '星期一',
	Tuesday = '星期二',
	Wednesday = '星期三',
	Thursday = 1,
	Friday = 2
}

这样做看起来很怪异,但或许在某些特殊的场合下会用到, 使用场景极少。

常量枚举值

枚举的值可以是常量也可以是计算值。 常量枚举值指的是在编译期间就可以取到值,计算值则需要在运行期间才能取到值。

Typescript对什么才是常量枚举值有如下的定义:

  1. 常量文字或者常量字符串
  2. 对已定义的常量枚举成员的引用
  3. 对常量枚举值进行+, -, *, /, ~ %, <<, >>, >>>, &, |,^算的也是属于常量枚举值

计算枚举值

除了上述三种情况,其他的所有情况都属于计算值, 计算值需要返回一个number

enum ConstantComputedMember {
	A = 1, // constant
	B, // constant
	C = 'abc', // constant
	D = 1 + 1, // constant
	F = 'a' + 'b', // constant
	G = 1 << 2, // constant
	H = A | B, // constant
	I = A & B, // constant
	J = [1, 2, 3].length, // computed
	K = getNum() // computed
}

枚举的编译结果

ts代码最终被编译成js才能运行,那么枚举编译后的结果是什么呢?

数字枚举的编译

enum Weekday {
	Monday,
	Tuesday,
	Wednesday,
	Thursday,
	Friday
}

经过编译后是下面的结果

"use strict";
var Weekday;
(function (Weekday) {
    Weekday[Weekday["Monday"] = 0] = "Monday";
    Weekday[Weekday["Tuesday"] = 1] = "Tuesday";
    Weekday[Weekday["Wednesday"] = 2] = "Wednesday";
    Weekday[Weekday["Thursday"] = 3] = "Thursday";
    Weekday[Weekday["Friday"] = 4] = "Friday";
})(Weekday || (Weekday = {}));

从上面的编译结果来看,我们不仅能使用Weekday.Monday得到枚举值0, 还能通过Weekday[Weekday.Monday]得到字符串"Monday"这个键名。这也是枚举的反向映射

字符串枚举的编译

enum Weekday {
	Monday = "Mon",
	Tuesday = "Tues",
	Wednesday = "Wednes",
	Thursday = "Thurs",
	Friday = "Fri"
}

经过编译后是下面的结果

"use strict";
var Weekday;
(function (Weekday) {
    Weekday["Monday"] = "Mon";
    Weekday["Tuesday"] = "Tues";
    Weekday["Wednesday"] = "Wednes";
    Weekday["Thursday"] = "Thurs";
    Weekday["Friday"] = "Fri";
})(Weekday || (Weekday = {}));

从上面的编译结果来看,我们可以通过Weekday.Friday来获取枚举值Fri, 但是我们不能用 Weekday[Weekday.Friday]来获取到字符串Friday , 这是因为具有字符串值的枚举成员不会生成反向映射

const 枚举

  • typescript会将常规枚举编译成一个对象与一个自执行的匿名函数,但是创建对象与执行函数需要一定额外的成本, 有没有一种办法枚举只执行枚举的工作,而不产生额外的数据,于是Typescript新增了const 枚举。

  • const 枚举的定义很简单,在常规枚举的定义enum前面加上 const 关键字即可。const 枚举只能用常量枚举,不能有计算值的枚举成员,而且const 枚举在编译期间完全被删除!

const enum Weekday {
	Monday,
	Tuesday,
	Wednesday,
	Thursday,
	Friday,
}

function foo(params: number) {
	if (Weekday.Friday === params) {
		return 'Friday'
	} else {
		return 'not Friday'
	}
}
foo(Weekday.Friday)

编译后的结果

"use strict";
function foo(params) {
	if (4 /* Weekday.Friday */ === params) {
		return 'Friday';
	}
	else {
		return 'not Friday';
	}
}

foo(4 /* Weekday.Friday */);

枚举Weekday没有产生额外的对象或函数,使用枚举成员的地方直接编译成了对应的值。

获取枚举键和值的类型

在开发自定义组件的时候,很多组件都会支持颜色属性,我们通常会定义这样的枚举类型。

enum Color {
	Red = '#ff0000',
	Green = '#00ff00',
	Blue = '#0000ff'
}
function changeTheme(color: Color) {
	this.color = color
}

但是为了更好的体验,可以允许用户传递进来如red这样的字符串,这个时候我们可以让我们定义的枚举类型的枚举成员的keyvalue都组装成联合类型。

type KeyTypeColor = Lowercase<keyof typeof Color>
function changeTheme(color: Color | KeyTypeColor) { // color: Color | "red" | "green" | "blue"
	this.theme = color
}
changeTheme('red')

扩展

vue3源码中通过枚举加位运算符进行组合权限认证。

源码中定义了ShapeFlags枚举类型描述了vnode的类型。

export const enum ShapeFlags {
	ELEMENT = 1, // 普通的HTML元素
	FUNCTIONAL_COMPONENT = 1 << 1, // 函数式组件
	STATEFUL_COMPONENT = 1 << 2, // 有状态组件
	TEXT_CHILDREN = 1 << 3, // 文本子节点
	ARRAY_CHILDREN = 1 << 4, // 数组子节点
	SLOTS_CHILDREN = 1 << 5, // 插槽子节点
	TELEPORT = 1 << 6, // teleport组件
	SUSPENSE = 1 << 7, // suspense组件
	COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, // 需要被keep-live的有状态组件
	COMPONENT_KEPT_ALIVE = 1 << 9, // 已经被keep-live的有状态组件
	COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT 
	// 组件,有状态组件和函数式组件的统称
}

它不仅效率很高,而且很方便的对vnode进行权限操作。

vnode授权

如上面代码所示COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT

此时代表COMPONENT被授予了ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT权限。

vnode鉴权

如果我们要判断一个vnode是否有COMPONENT权限,只需要COMPONENT & VnodeType 只要为0就代表没有权限。

const hasPer = ShapeFlags.COMPONENT & ShapeFlags.ELEMENT

console.log(hasPer) // 0

hasPer为0表示没有权限

const hasPer = ShapeFlags.COMPONENT & ShapeFlags.FUNCTIONAL_COMPONENT

console.log(hasPer) // 2

hasPer不为0表示有权限。

总结

相信通过此文你已经掌握枚举了。

images.jpeg