关于TS的枚举,你都知道吗?

在 TypeScript 中,enum 关键字提供了一种定义一组命名常量的方式,这些常量可以作为类型或值使用。枚举最早在 TypeScript 的第一个版本中引入,虽然它们还没有被添加到 JavaScript 中,但它们在 TypeScript 中作为一个独有的运行时特性,展现了强大的功能和一些有趣的行为。

文末有我针对前端面试的多年经验文章,欢迎大家积极发言!!!

枚举特别适用于定义一组有限的、稳定的常量值,帮助开发者编写更加清晰和易于维护的代码。

数值枚举

数值枚举是一种将相关的成员组合在一起,并为它们自动分配从 0 开始的数值的机制。例如,以下是一个定义了 AlbumStatus 枚举的代码:

enum AlbumStatus {
  NewRelease,
  OnSale,
  StaffPick
}

在这个例子中,AlbumStatus.NewRelease的值为 0,AlbumStatus.OnSale为 1,依此类推。我们可以通过名称来使用这个枚举:

function logStatus(status: AlbumStatus) {
  console.log(status);
  // 输出 0
}
logStatus(AlbumStatus.NewRelease);

当然,你也可以为枚举的每个成员指定特定的值:

enum AlbumStatus {
  NewRelease = 1,
  OnSale = 2,
  StaffPick = 3
}

此时,AlbumStatus.NewRelease 的值为 1,AlbumStatus.OnSale 为 2,依次类推。

自动递增的数值枚举

当你只为枚举的某些成员指定了值时,TypeScript 会从最后一个指定值开始自动递增。例如:

enum AlbumStatus {
  NewRelease = 1,
  OnSale,
  StaffPick,
}

在这个例子中,OnSale 会自动分配为 2,StaffPick 则为 3。

字符串枚举

字符串枚举允许你为枚举的每个成员分配字符串值,这在某些场景中会更具表达力:

enum AlbumStatus {
  NewRelease = "NEW_RELEASE",
  OnSale = "ON_SALE",
  StaffPick = "STAFF_PICK",
}

使用字符串枚举后,logStatus函数将输出字符串值:

function logStatus(status: AlbumStatus) {
  console.log(status);
  // 输出 "NEW_RELEASE"
}
logStatus(AlbumStatus.NewRelease);

枚举的奇特行为

由于 JavaScript 本身没有 enum 关键字的等效语法,TypeScript 在为枚举制定规则时展现了一些独特的行为。

数值枚举的反向映射

当 TypeScript 编译数值枚举时,会生成一个支持反向映射的对象。这意味着你不仅可以通过枚举名获取数值,还可以通过数值反向获取枚举名。例如:

enum AlbumStatus {
  NewRelease,
  OnSale,
  StaffPick,
}

会被编译为以下 JavaScript 代码:

var AlbumStatus;
(function (AlbumStatus) {
  AlbumStatus[AlbumStatus["NewRelease"] = 0] = "NewRelease";
  AlbumStatus[AlbumStatus["OnSale"] = 1] = "OnSale";
  AlbumStatus[AlbumStatus["StaffPick"] = 2] = "StaffPick";
})(AlbumStatus || (AlbumStatus = {}));

这个代码段为每个枚举值创建了一个对象属性,同时生成了一个值到键的反向映射:

var AlbumStatus = {
  0: "NewRelease",
  1: "OnSale",
  2: "StaffPick",
  NewRelease: 0,
  OnSale: 1,
  StaffPick: 2,
};

因此,当你调用Object.keys(AlbumStatus)时,将会得到如下输出:

console.log(Object.keys(AlbumStatus));
// ["0", "1", "2", "NewRelease", "OnSale", "StaffPick"]

字符串枚举的简化行为

相比之下,字符串枚举的行为要简单得多。它不会生成反向映射,只保留枚举值:

enum AlbumStatus {
  NewRelease = "NEW_RELEASE",
  OnSale = "ON_SALE",
  StaffPick = "STAFF_PICK",
}

编译后生成的 JavaScript 代码为:

var AlbumStatus;
(function (AlbumStatus) {
  AlbumStatus["NewRelease"] = "NEW_RELEASE";
  AlbumStatus["OnSale"] = "ON_SALE";
  AlbumStatus["StaffPick"] = "STAFF_PICK";
})(AlbumStatus || (AlbumStatus = {}));

调用 Object.keys(AlbumStatus) 只会返回键值:

console.log(Object.keys(AlbumStatus));
// ["NewRelease", "OnSale", "StaffPick"]

这种数值枚举和字符串枚举之间的差异可能会引起一定的困惑。

类型差异

数值枚举和字符串枚举在用作类型时表现不同。让我们用数值枚举来定义 logStatus 函数:

enum AlbumStatus {
  NewRelease = 0,
  OnSale = 1,
  StaffPick = 2,
}

function logStatus(status: AlbumStatus) {
  console.log(status);
}

你可以直接传入枚举成员,也可以传入一个对应的数字:

logStatus(AlbumStatus.NewRelease);
logStatus(0);

但是,如果传入一个非枚举成员的数字,TypeScript 会报错:

logStatus(3);
// Argument of type '3' is not assignable to parameter of type 'AlbumStatus'.

相比之下,字符串枚举只允许枚举成员作为类型:

enum AlbumStatus {
  NewRelease = "NEW_RELEASE",
  OnSale = "ON_SALE",
  StaffPick = "STAFF_PICK",
}

function logStatus(status: AlbumStatus) {
  console.log(status);
}

logStatus(AlbumStatus.NewRelease);
logStatus("NEW_RELEASE");
// Argument of type '"NEW_RELEASE"' is not assignable to parameter of type 'AlbumStatus'.

字符串枚举的行为更接近其他语言(如 C# 和 Java)中的枚举,但它们与数值枚举的不一致性可能会让人感到困惑。特别是字符串枚举在 TypeScript 中是基于名称(名义)进行比较的,而不是像其他类型那样基于结构进行比较。这意味着即使两个字符串枚举的成员相同,但名称不同,它们也会被视为不同的类型:

enum AlbumStatus2 {
  NewRelease = "NEW_RELEASE",
  OnSale = "ON_SALE",
  StaffPick = "STAFF_PICK",
}

logStatus(AlbumStatus2.NewRelease);
// Argument of type 'AlbumStatus2.NewRelease' is not assignable to parameter of type 'AlbumStatus'.

const 枚举

const enum 允许你在声明枚举时使用 const 关键字,这种枚举在编译时会直接被替换为相应的值,不会生成任何额外的对象。使用 const enum 的代码如下:

const enum AlbumStatus {
  NewRelease = "NEW_RELEASE",
  OnSale = "ON_SALE",
  StaffPick = "STAFF_PICK",
}

编译后,TypeScript 会直接将枚举的值替换到代码中:

let albumStatuses = [
  "NEW_RELEASE",
  "ON_SALE",
  "STAFF_PICK",
];

不过,TypeScript 团队建议在库代码中避免使用 const enum,因为它们可能会在库的使用者那里表现出不可预测的行为。

总结

在这篇文章中,我们探讨了 TypeScript 枚举的几种形式及其特性:

  1. 数值枚举 具有反向映射的特性,并且类型不具备强制性,可以用具体值代替。
  2. 字符串枚举 不具备反向映射,但类型具有强相等性,不会进行结构对比,也不能直接用值替代。
  3. 常量枚举 在编译后会被替换成具体的值,而不会生成额外的 JavaScript 代码。

通过合理地使用枚举,你可以让代码更加简洁、清晰,同时增强代码的可维护性。

希望这篇文章对你有所帮助!接下来给大家推荐一篇我针对前端面试的多年经验文章,希望大家看完以后都可以领取到心仪的offer哦!

文章:《聊聊前端面试那些事儿》

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值