如何在 TypeScript 中获取枚举名称:引擎盖下的魔法

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:Stack Overflow 上的永恒问题



您是否曾好奇过,经验丰富的 TypeScript 开发者是如何获取 enum 值的名称的?如果您浏览过 Stack Overflow,很可能见过那个拥有数千次浏览的问题:"How to get names of enum entries?"。看起来似乎并不复杂?但实际上,许多新手(甚至一些老手)都会在这里栽跟头。



在本文中,我们不仅会给出现成的答案。我们还会深入 TypeScript 的底层,剖析 enum 的具体工作原理,编译器如何将其转换为 JavaScript,以及为什么某些获取名称的方法会导致意想不到的错误。准备好了吗?我们开始吧!



什么是 TypeScript 中的枚举,为什么需要它们的名称?



枚举(Enum)是一种定义一组命名常量的便捷方式。在 TypeScript 中,枚举分为数字型(默认)和字符串型。但是,如果您需要获取某个特定值的名称该怎么办?例如,您从 API 获取了状态码 403,但希望向用户显示一条易懂的消息 "Forbidden"。



enum HttpStatus {  OK = 200,  NotFound = 404,  Forbidden = 403}

// 我们的任务:根据值 403 获取名称 "Forbidden"


看起来 TypeScript 应该为此提供一个内置方法。但事实并非如此。您需要自己编写逻辑。而有趣的部分就从这里开始。



“直接”解决方案:Object.keys 和 Object.values



最显而易见的方法是使用 Object.keys() 或 Object.values() 方法。许多新手会写出类似这样的代码:



function getEnumName(enumObj: any, value: number): string | undefined {  const keys = Object.keys(enumObj);  return keys.find(key => enumObj[key] === value);}

console.log(getEnumName(HttpStatus, 403)); // "Forbidden" — 有效!


看起来合情合理?是的。但如果您在数字型枚举上尝试这段代码,将会遇到一个惊喜。让我们看看我们的 HttpStatus 被编译成了什么。



底层揭秘:数字型枚举的编译



当 TypeScript 编译数字型枚举时,它会创建一个具有反向映射(reverse mapping)的对象。以下是编译后的 JavaScript 代码:



var HttpStatus;(function (HttpStatus) {    HttpStatus[HttpStatus["OK"] = 200] = "OK";    HttpStatus[HttpStatus["NotFound"] = 404] = "NotFound";    HttpStatus[HttpStatus["Forbidden"] = 403] = "Forbidden";})(HttpStatus || (HttpStatus = {}));

// 最终 HttpStatus 对象看起来像这样:// { // "200": "OK", // "404": "NotFound", // "403": "Forbidden", // "OK": 200, // "NotFound": 404, // "Forbidden": 403 // }


看到诀窍了吗?编译器同时添加了数字键和字符串键。因此,Object.keys(HttpStatus) 返回的不仅仅是 ["OK", "NotFound", "Forbidden"],还包括 ["200", "404", "403"]。这破坏了我们简单解决方案的逻辑!



要解决这个问题,需要过滤掉数字键:



function getEnumNameSafe(enumObj: any, value: number): string | undefined {  const keys = Object.keys(enumObj).filter(key => isNaN(Number(key)));  return keys.find(key => enumObj[key] === value);}


但这样的代码看起来像是个补丁。有没有更好的方法?



字符串枚举:另一种情况



如果您使用字符串枚举,则不会创建反向映射。这简化了任务:



enum Direction {  Up = "UP",  Down = "DOWN",  Left = "LEFT",  Right = "RIGHT"}

// Object.keys(Direction) 只会返回 ["Up", "Down", "Left", "Right"] — 完美!


但是,如果您需要根据值获取名称呢?同样的 Object.keys 方法也能很好地工作:



function getStringEn                

博客

书籍推荐