TypeScript泛型自定义Enum写法冗余,求更简洁实现方案
简化TypeScript自定义泛型Enum的写法
我完全懂你这种重复写法的痛苦!每次写泛型Enum都要重复一遍类型,还要加个as交叉类型,真的很繁琐。这里有几个更简洁的方案,既能让IDE正确识别FolderVisibility.PUBLIC === 'public'这类类型判断,又能保留自定义Enum类的自有属性:
方案1:用工厂函数自动推断类型
首先假设你的自定义Enum类是类似这样的(带自有方法的版本):
class Enum<T extends Record<string, string | number>> { private readonly valuesMap: T; constructor(values: T) { this.valuesMap = Object.freeze({ ...values }); // 将枚举键值对挂载到实例上 Object.assign(this, values); } // 自定义方法:获取所有枚举值 values(): Array<T[keyof T]> { return Object.values(this.valuesMap); } // 自定义方法:校验值是否为合法枚举值 isValid(value: unknown): value is T[keyof T] { return this.values().includes(value as T[keyof T]); } }
接下来我们写一个工厂函数,帮我们自动处理类型推断和交叉类型,不用手动重复代码:
function createEnum<T extends Record<string, string | number>>(values: T): Enum<T> & T { return new Enum(values) as Enum<T> & T; }
现在创建枚举就变得非常简洁了,TypeScript会自动推断所有类型:
const FolderVisibility = createEnum({ PUBLIC: 'public', PRIVATE: 'private' });
此时IDE会正确识别FolderVisibility.PUBLIC的类型是'public',同时你也能正常调用Enum类的自有方法,比如FolderVisibility.values()或者FolderVisibility.isValid('public')。
方案2:优化Enum类的构造函数返回类型
如果你不想额外写工厂函数,也可以直接修改Enum类的构造函数,让它返回正确的交叉类型,这样创建实例时一步到位:
class Enum<T extends Record<string, string | number>> { private readonly valuesMap: T; constructor(values: T) { this.valuesMap = Object.freeze({ ...values }); Object.assign(this, values); // 断言返回类型为Enum实例与枚举键值对的交叉类型 return this as unknown as Enum<T> & T; } values(): Array<T[keyof T]> { return Object.values(this.valuesMap); } isValid(value: unknown): value is T[keyof T] { return this.values().includes(value as T[keyof T]); } }
现在直接创建枚举即可,不需要任何额外的类型断言或重复泛型参数:
const FolderVisibility = new Enum({ PUBLIC: 'public', PRIVATE: 'private' });
两种方案都能实现你的需求:
- 避免了重复的泛型参数和类型断言
- IDE能精准识别每个枚举属性的具体值类型
- 枚举参数通过
Object.freeze保持只读状态
内容的提问来源于stack exchange,提问作者Akxe
相关产品推荐
相关产品推荐

