• 作者:老汪软件技巧
  • 发表时间:2024-11-05 11:01
  • 浏览量:

前言

我们知道,在JavaScript中是不存在枚举的,但Typescript可以使用enum关键字定义的枚举值,但是你有没有真正去了解不同的枚举值都具有哪些潜在行为?下面将细数不同的枚举类型的实现方式,以及为什么我不建议你使用数值作为枚举值。

三种枚举类型探究

你可以为枚举声明这几种不同的类型,分别为字符串、数值、混合类型枚举值。

数值枚举探究

// 数值枚举
enum NumberEnum {
  Pass = 1,
  Deny = 2,
  Pending = 3
}
// 以上ts枚举代码编译为js代码
var NumberEnum;
(function (NumberEnum) {
    NumberEnum[NumberEnum["Pass"] = 1] = "Pass";
    NumberEnum[NumberEnum["Deny"] = 2] = "Deny";
    NumberEnum[NumberEnum["Pending"] = 3] = "Pending";
})(NumberEnum || (NumberEnum = {}));
// 转换为js对象后,是具有六个属性的对象,分别为每一个键、值对的创建映射关系
console.log(Object.keys(NumberEnum))
// [ '1', '2', '3', 'Pass', 'Deny', 'Pending' ]

字符串枚举探究

// 字符串枚举
enum StringEnum {
  Pass = "VPass",
  Deny = "VDeny",
  Pending = "VPending"
}
// 以上ts枚举代码编译为js代码
var StringEnum;
(function (StringEnum) {
    StringEnum["Pass"] = "VPass";
    StringEnum["Deny"] = "VDeny";
    StringEnum["Pending"] = "VPending";
})(StringEnum || (StringEnum = {}));
// 只有三个属性,不会为每个枚举值值创建一个对象属性
console.log(Object.keys(StringEnum))
// [ 'Pass', 'Deny', 'Pending' ]

混合值枚举探究

// 混合枚举
enum MixNumberStringEnum {
  Pass = "VPass",
  Deny = 2,
  Pending = "VPending"
}
// 以上ts枚举代码编译为js代码
var MixNumberStringEnum;
(function (MixNumberStringEnum) {
    MixNumberStringEnum["Pass"] = "VPass";
    MixNumberStringEnum[MixNumberStringEnum["Deny"] = 2] = "Deny";
    MixNumberStringEnum["Pending"] = "VPending";
})(MixNumberStringEnum || (MixNumberStringEnum = {}));
// 只会为数值枚举值创建键值对映射关系
console.log(Object.keys(MixNumberStringEnum));
// [ '2', 'Pass', 'Deny', 'Pending' ]

得出结论: 数值和字符串枚举值具有不同的表现。只有枚举值为数值时,才会创建值->键的映射关系;当枚举值为字符串时,只有键->值的映射关系,并不会创建值->键的映射关系

枚举作为函数参数类型时的表现形式

/* 1.字符串枚举值作为参数 */
enum StringEnum {
  Pass = "VPass",
  Deny = "VDeny",
  Pending = "VPending"
}
const updateString = (status: StringEnum) => {
  console.log(status)
}
// ❌错误:类型“"VPass"”的参数不能赋给类型“StringEnum”的参数。
updateString("VPass")
// ✔正确用法
updateString(StringEnum.Pass)
/* 2.数值枚举值作为参数 */
enum NumberEnum {
  Pass = 1,
  Deny = 2,
  Pending = 3
}
const updateNumber = (status: NumberEnum) => {
  console.log(status)
}
// ✔正确(但不推荐):数值枚举可以直接传递数值参数
updateNumber(1)
// ✔正确(推荐用法)
updateNumber(NumberEnum.Pass)

通过以上字符串枚举类型和数值枚举类型的对比中可得出结论:

枚举值的作用__枚举型数据是什么意思

当参数为字符串枚举类型时。即使通过硬编码传递的参数值和枚举值完全相等也会发生类型错误(这个错误应该是正确的!)。

当参数为数值类型时。则不会出现字符串枚举值的问题(没有问题反而是问题)。

note: 其实按理说为数值时,应该也要报错才是,这可能是ts设计上的一个问题。我们在开发中也应尽量避免这种硬编码的方式书写代码,避免出现一些奇异的问题!

枚举不存在引用关系

enum StringEnum {
  Pass = "VPass",
  Deny = 2,
  Pending = "VPending"
}
enum StringEnum2 {
  Pass = StringEnum.Pass,
  Deny = StringEnum.Deny
}
// 字符串枚举值作为参数
const updateString = (status: StringEnum) => {
  console.log(status)
}
// ❌错误:必须严格遵守参数类型对应的枚举对象StringEnum
updateString(StringEnum2.Pass)
// ❌错误:必须严格遵守参数类型对应的枚举对象StringEnum
updateString(StringEnum2.Deny)

枚举推荐用法

当枚举值为数值时,由于TypeScript枚举的实现方式会隐式创建值->键的隐式关系,这一点很容易让人从视觉感官上忽略这个问题。除此之外,也会出现诸如前面“数值枚举探究”当中提到的问题。

所以,当使用枚举时更推荐使用字符串作为枚举值。因为它是显性定义的,所见即所得,更符合我们的编程习惯!更能避免一些不必要的问题!

如下使用示例:

// 把数字变为字符串,显式定义枚举值,避免隐式创建值->键的映射关系
enum StringEnum {
  Pass = "1",
  Deny = "2",
  Pending = "3"
}
console.log(Object.keys(StringEnum))
// [ 'Pass', 'Deny', 'Pending' ]

总结

在TypeScript中,定义的枚举值为数值和字符串具有不同的表现形式。当枚举值为数值时,会隐式创建值->键的隐式关系,从主观上来看,容易使人忽略其内在实现方式;并且当数值枚举作为参数时,通过硬编码的方式传递参数也没有正确的错误提示。为了写出更符合我们编程习惯的代码,更推荐使用字符串作为枚举值的用法!