如何在TypeScript中实现枚举通用选择项生成函数?
问题
我有多个TypeScript枚举,目前为每个枚举都写了重复的转换函数,示例如下:
getMyEnumSelectItems(): Observable<SelectItem[]> { const values = Object.values(MyEnum); return this.translate .stream(values.map((val) => `myEnum.${val}`)) .pipe( map((translatedValues) => values.map((val) => ({ label: translatedValues[`myEnum.${val}`] || val, value: val })) ) ); }
现在我想写一个通用的泛型函数来统一处理所有枚举,预期的伪代码如下:
getSelectItems<T extends Enum>(): Observable<SelectItem[]> { const values = Object.values(T); const camelCaseName = nameof(T).toCamelCase(); return this.translate .stream(values.map((val) => `${camelCaseName}.${val}`)) .pipe( map((translatedValues) => values.map((val) => ({ label: translatedValues[`${camelCaseName}.${val}`] || val, value: val })) ) ); }
希望调用方式为:const selectItems = getSelectItems<MyEnum>();,请问有没有可行的实现方案?
可行方案
TypeScript的泛型类型信息在运行时会被擦除,无法直接通过泛型参数T获取枚举的名称和值。要实现通用函数,必须把枚举本身作为参数传入,再配合泛型约束保证类型安全。具体实现如下:
1. 定义泛型约束与辅助函数
先约束枚举类型的结构,再写一个大驼峰转小驼峰的辅助函数:
// 约束参数为枚举类型(键值对结构,值为字符串或数字) type EnumType = Record<string, string | number>; // 大驼峰转小驼峰的辅助函数 const toCamelCase = (str: string): string => { return str.charAt(0).toLowerCase() + str.slice(1); };
2. 实现通用转换函数
基于原有逻辑抽象出通用函数,通过传入的枚举对象获取值和名称:
getSelectItems<T extends EnumType>(enumObj: T): Observable<SelectItem[]> { // 获取枚举的所有值,针对数字枚举可添加过滤逻辑 const values = Object.values(enumObj) as Array<T[keyof T]>; // 获取枚举名称并转为小驼峰 const enumName = enumObj.constructor.name; const camelCaseName = toCamelCase(enumName); return this.translate .stream(values.map((val) => `${camelCaseName}.${val}`)) .pipe( map((translatedValues) => values.map((val) => ({ label: translatedValues[`${camelCaseName}.${val}`] || String(val), value: val })) ) ); }
3. 调用方式
调用时直接传入枚举对象即可,TypeScript会自动推导泛型类型:
const selectItems = this.getSelectItems(MyEnum);
关键细节
- 数字枚举处理:如果是数字枚举,
Object.values会同时返回字符串类型的键和数字值,需要添加过滤逻辑:const values = Object.values(enumObj).filter(val => typeof val === 'number') as Array<T[keyof T]>; - 类型安全:泛型约束
T extends EnumType保证传入的参数是枚举类型,返回的SelectItem数组的value类型会匹配枚举的值类型,避免类型错误。 - 枚举名称可靠性:
enumObj.constructor.name能稳定获取枚举的原始名称(比如MyEnum),前提是编译时没有开启代码混淆类名的选项。
内容的提问来源于stack exchange,提问作者MatterOfFact
相关产品推荐
相关产品推荐

