尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

别再只用for-in了!盘点TypeScript中Enum遍历的5种姿势及其适用场景

别再只用for-in了!盘点TypeScript中Enum遍历的5种姿势及其适用场景 别再只用for-in了盘点TypeScript中Enum遍历的5种姿势及其适用场景TypeScript的枚举Enum是构建可维护代码的利器但许多开发者至今仍停留在for-in循环的舒适区。实际上根据不同的业务场景至少有5种更优雅的遍历方案值得掌握。本文将带你解锁这些技巧并附上性能对比和实战场景分析。1. 为什么需要多种遍历方式枚举本质上是一种特殊的对象结构。以这个颜色枚举为例enum Colors { Red #FF0000, Green #00FF00, Blue #0000FF }当我们需要实现以下功能时就需要不同的遍历策略生成颜色选择器下拉菜单需要键值对验证用户输入是否为有效枚举值需要值列表动态生成API文档需要键列表序列化枚举数据需要特定格式转换数字枚举会有反向映射的特性这使得遍历逻辑需要特殊处理。比如enum Status { Pending, Approved }会被编译为{0: Pending, 1: Approved, Pending: 0, Approved: 1}2. 基础方案for-in循环的陷阱与技巧for-in是最常见的遍历方式但存在几个关键注意事项// 获取所有key for (const key in Colors) { if (Colors.hasOwnProperty(key)) { console.log(key); // 输出Red, Green, Blue } } // 获取所有value for (const key in Colors) { const value Colors[key as keyof typeof Colors]; console.log(value); // 输出#FF0000, #00FF00, #0000FF }数字枚举的特殊处理enum StatusCode { NotFound 404, ServerError 500 } for (const key in StatusCode) { // 必须过滤掉反向映射的值 if (isNaN(Number(key))) { console.log(${key}: ${StatusCode[key as keyof typeof StatusCode]}); } }方案优点缺点for-in兼容性好需要手动过滤数字枚举的反向映射Object.keys代码简洁同样需要处理数字枚举问题3. 现代方案Object API三剑客ES6引入的Object.keys/values/entries提供了更函数式的选择// 获取键数组 const colorKeys Object.keys(Colors).filter(key isNaN(Number(key))); // 输出: [Red, Green, Blue] // 获取值数组 const colorValues Object.values(Colors).filter( (val, index, arr) arr.indexOf(val) index ); // 输出: [#FF0000, #00FF00, #0000FF] // 获取键值对 const colorEntries colorKeys.map(key [key, Colors[key as keyof typeof Colors]]); // 输出: [[Red, #FF0000], ...]性能提示在V8引擎中Object.values比手动for-in循环快约15%但在热代码路径中差异可以忽略不计。4. 反射方案Reflect.ownKeys的妙用Reflect.ownKeys可以获取包括Symbol在内的所有属性键enum MixedEnum { A 1, B B, [Symbol(secret)] hidden } const keys Reflect.ownKeys(MixedEnum); // 输出: [A, B, 1, B, Symbol(secret)]注意这个方法会返回所有可枚举和不可枚举属性通常需要配合typeof类型守卫来过滤不需要的条目。5. 高级方案生成器函数实现迭代协议对于需要复杂遍历逻辑的场景可以实现迭代器协议function* enumEntriesT extends object(enumObj: T) { const knownKeys new Setstring(); for (const key in enumObj) { if (!isNaN(Number(key))) continue; if (knownKeys.has(key)) continue; knownKeys.add(key); yield [key, enumObj[key]] as const; } } // 使用示例 for (const [key, value] of enumEntries(Colors)) { console.log(${key} - ${value}); }这种方案特别适合需要惰性计算的场景超大枚举的内存优化自定义过滤逻辑如按前缀过滤6. 实战场景选型指南根据具体需求选择最佳方案表单下拉菜单生成// 需要键值对数组 const options Object.keys(Colors) .filter(key isNaN(Number(key))) .map(key ({ label: key, value: Colors[key as keyof typeof Colors] }));API参数验证// 需要值集合 const validValues new Set( Object.values(Colors).filter((v, i, arr) arr.indexOf(v) i) ); function isValidColor(input: string) { return validValues.has(input); }动态类型生成// 需要键集合 type ColorKeys keyof typeof Colors; const keys: ColorKeys[] Object.keys(Colors).filter( k isNaN(Number(k)) ) as ColorKeys[];在大型项目中建议将这些遍历逻辑封装为工具函数。比如// enum-utils.ts export function getEnumKeysT extends object(enumObj: T) { return Object.keys(enumObj).filter( key isNaN(Number(key)) ) as Arraykeyof T; } export function getEnumValuesT extends object(enumObj: T) { const keys getEnumKeys(enumObj); return keys.map(key enumObj[key]); }这些方案各有优劣关键是根据具体场景选择最合适的方式。在需要处理超大枚举时生成器方案能提供更好的内存效率而在简单场景下Object.values可能是最简洁的选择。
返回列表