不要在 Typescript 中使用枚举,它们非常危险😨
你有没有想过为什么 TypeScript 专家建议避免使用 ENUM?虽然它们看起来像是定义一组常量值的有用工具,但实际上非常危险。在本文中,我们将向你展示为什么以及如何避免使用它们。你会发现有很多更安全可靠的替代方案可供选择。准备好迎接惊喜吧!😱
枚举的发射代码
建议不要使用它的原因之一是,它在编译应用程序时会生成代码。ENUM 会在编译时生成额外的代码,这会增加最终文件的大小。这可能会对应用程序的加载速度和性能产生负面影响。
定义我们的角色枚举
enum Roles {
Admin,
Writer,
Reader
}
输出(构建生成的代码)
var Roles;
(function (Roles) {
Roles[Roles["Admin"] = 0] = "Admin";
Roles[Roles["Writer"] = 1] = "Writer";
Roles[Roles["Reader"] = 2] = "Reader";
})(Roles|| (Roles = {}));
虽然如果使用常量枚举,这个细节确实是固定的,但我参与过多个项目,看到人们到处使用常规枚举,并想知道为什么他们的输出如此之大。
Postada:我曾经也是其中之一。😁
这看起来可能微不足道,但想象一下,如果您在“前端”和“后端”之间共享文件,您最终可能会得到相当重的捆绑包。
好的,这是一方面,我们可以通过强制使用常量来解决这个问题。但这也带来了令人不快的歧义。
数字类型usafe
是的,你没看错。这不是标题党。常规的数字枚举(例如不设置字符串值的枚举)并非类型安全!如果我们回顾一下之前的 Roles 枚举,就会发现接受用户角色的函数实际上也可以接受任意数字值。
enum Roles {
Admin,
Writer,
Reader
}
declare function hasAccess(role: Roles): void;
hasAccess(10);
// ☝️ Worst of all, this is ok! 😱
您可能已经注意到,当hasAccess(10)
调用该函数时,传递的数值不属于枚举 Roles,这在 TypeScript 中是允许的,而这被视为问题,因为它允许输入意外和未经验证的值,这可能会导致应用程序出现安全和性能问题。
字符串 ENUM 是命名类型
在结构类型盛行的时代,枚举类型选择成为命名类型。这意味着,即使值有效且受支持,它们也无法传递给需要字符串枚举的函数或对象。让我们看这个例子:
enum Roles {
Admin = 'admin',
Writer = 'writer',
Reader = 'reader'
}
declare function hasAccess(role: Roles): void;
hasAccess('admin') // Invalid.
hasAccess(Roles.Admin) // Valid.
如您所见,枚举是一种命名类型,并且仅接受特定于枚举的值,而不是兼容或相似的值,这可能导致兼容性问题,在设计和使用枚举时应仔细考虑。
解决方案
一个更安全、更能保证兼容性的替代方案是使用对象。让我们看下面的例子:
const Roles = {
Admin: "admin",
Writer: "writer",
Reader: "reader"
} as const;
// Convert object key in a type
type RoleKeys = typeof Roles[keyof typeof Roles]
declare function hasAccess(role: RoleKeys): void;
// 💥 Error!
move('guest');
// 👍 Great!
move('admin');
// 👍 Also great!
move(Roles.Admin);
结论
枚举 (ENUM) 看似是一个定义常量值的有用工具,但实际上却非常危险。过度使用常规枚举可能会导致代码大小问题、安全性问题、可扩展性和可维护性问题。
与其使用枚举,不如选择对象或类型。对象灵活且可扩展,这意味着它们可以在运行时修改并添加新值。类型也同样灵活且可扩展,并且能够提供更好的代码清晰度和可读性。此外,与枚举相比,对象和类型更不容易出现错误和安全问题。简而言之,就灵活性、可扩展性、清晰度、可读性和安全性而言,使用对象或类型代替枚举是更好的选择。