🎯 前言
在日常开发中,我们经常需要定义各种枚举类型来表示状态、类型等常量值。TypeScript 原生的 enum
虽然功能强大,但在某些场景下仍有一些局限性。今天分享一个 constructEnum
工具函数,它能让枚举使用更加灵活和类型安全。💪
😤 传统 TypeScript 枚举的痛点
1. 缺乏元数据支持 📝
typescript
enum UserRole {
ADMIN = 'ADMIN',
EDITOR = 'EDITOR',
VIEWER = 'VIEWER'
}
// 如果我们需要显示中文标签,通常需要额外的映射 😰
const roleLabels = {
[UserRole.ADMIN]: '管理员',
[UserRole.EDITOR]: '编辑者',
[UserRole.VIEWER]: '查看者'
}
2. 混合类型支持不够灵活 🤔
原生枚举要么全是字符串,要么全是数字,很难在一个枚举中同时使用不同类型。
3. 运行时信息获取困难 😵
当我们需要根据枚举值获取对应的元数据时,通常需要额外的映射对象,代码分散且不够优雅。
💡 constructEnum 的设计思路
constructEnum
的核心思想是将枚举值和元数据统一管理,同时保持完整的 TypeScript 类型推导。🎉
⭐ 核心特性
- 统一的配置对象:支持简单值和复杂对象混合定义 🔗
- 完整的类型推导:保持 TypeScript 类型安全 🛡️
- 元数据获取:提供便捷的信息获取方法 📊
- 灵活的值类型:支持字符串、数字等多种类型 🔢
🔧 实现解析
🎨 类型系统设计
typescript
// 枚举配置的类型定义
type EnumConfig = {
[K: string]: number | string | { value: string | number; label?: string; [key: string]: any }
}
// 枚举对象的类型定义
type EnumObject<T extends EnumConfig> = {
[K in keyof T]: T[K] extends string | number ? T[K] : T[K] extends { value: string } ? T[K]['value'] : never
}
// 获取配置中所有对象类型的并集
type ExtractObjectConfigs<T extends EnumConfig, P extends keyof EnumObject<T>> = {
[K in keyof T]: T[K] extends { value: string } ? T[P] : never
}[keyof T]
// 枚举信息获取函数的类型定义
type EnumInfoGetter<T extends EnumConfig> = <P extends keyof EnumObject<T>>(
enumValue: P
) => ExtractObjectConfigs<T, P>
⚡ 核心实现
typescript
function constructEnum<T extends EnumConfig>(config: T): [EnumObject<T>, EnumInfoGetter<T>] {
// 创建枚举对象
const enumObj = {} as EnumObject<T>
// 创建值到配置的映射
const valueToConfig = new Map<string, any>()
// 遍历配置,构建枚举对象和映射
for (const [key, value] of Object.entries(config)) {
if (typeof value !== 'object') {
enumObj[key as keyof T] = value as EnumObject<T>[keyof T]
valueToConfig.set(value.toString(), value)
} else if (value && typeof value === 'object' && 'value' in value) {
enumObj[key as keyof T] = value.value as EnumObject<T>[keyof T]
valueToConfig.set(value.value.toString(), value)
}
}
// 创建获取枚举信息的函数
const getEnumInfo: EnumInfoGetter<T> = <K extends keyof EnumObject<T>>(enumValue: K) => {
return valueToConfig.get(enumValue as any)
}
return [enumObj, getEnumInfo]
}
🛠️ 使用场景和优势
1. 简单枚举场景 🎯
typescript
const [OrderStatus, getOrderStatusInfo] = constructEnum({
PENDING: 'PENDING',
PROCESSING: 'PROCESSING',
COMPLETED: 'COMPLETED',
CANCELLED: 'CANCELLED'
})
console.log(OrderStatus.PENDING) // 'PENDING'
console.log(getOrderStatusInfo('PENDING')) // 返回原始值
2. 带元数据的复杂枚举 🏆
typescript
const [UserRole, getUserRoleInfo] = constructEnum({
ADMIN: {
value: 'ADMIN',
label: '管理员',
permissions: ['read', 'write', 'delete'],
level: 1
},
EDITOR: {
value: 'EDITOR',
label: '编辑者',
permissions: ['read', 'write'],
level: 2
},
VIEWER: {
value: 'VIEWER',
label: '查看者',
permissions: ['read'],
level: 3
}
})
// 使用枚举值
console.log(UserRole.ADMIN) // 'ADMIN'
// 获取完整元数据 ✨
const adminInfo = getUserRoleInfo('ADMIN')
// 返回: { value: 'ADMIN', label: '管理员', permissions: [...], level: 1 }
3. 混合类型枚举 🌈
typescript
const [NotificationType, getNotificationInfo] = constructEnum({
INFO: 1, // 数字类型
SUCCESS: {
value: 'SUCCESS',
label: '成功',
icon: 'check-circle',
color: '#52c41a'
},
WARNING: {
value: 'WARNING',
label: '警告',
icon: 'exclamation-circle',
color: '#faad14'
}
})
🎉 constructEnum 的核心优势
1. 📦 统一的数据管理
- 枚举值和元数据在同一个配置对象中定义
- 避免了多个映射对象分散管理的问题
- 降低了维护成本
2. 🛡️ 完整的类型安全
- 利用 TypeScript 高级类型特性,提供精确的类型推导
- 编译时就能发现类型错误
- IDE 自动补全支持
3. 🔄 灵活的配置方式
- 支持简单值(字符串、数字)
- 支持复杂对象(包含任意元数据)
- 同一枚举内可混合使用不同配置方式
4. 🚀 便捷的信息获取
- 通过
getEnumInfo
函数轻松获取元数据 - 类型安全的信息访问
- 支持运行时动态查询
5. 📈 良好的扩展性
- 元数据对象支持任意字段扩展
- 不影响现有代码的向后兼容
- 易于集成到现有项目中
🌟 实际应用场景
1. 表单选项配置 📋
typescript
const [GenderOptions, getGenderInfo] = constructEnum({
MALE: {
value: 'M',
label: '男',
icon: 'male'
},
FEMALE: {
value: 'F',
label: '女',
icon: 'female'
}
})
// 在 React 组件中使用
const options = Object.keys(GenderOptions).map(key => {
const value = GenderOptions[key]
const info = getGenderInfo(value)
return {
value,
label: info.label,
icon: info.icon
}
})
2. 状态管理 🔄
typescript
const [TaskStatus, getTaskStatusInfo] = constructEnum({
TODO: {
value: 'TODO',
label: '待办',
color: '#1890ff',
allowedTransitions: ['IN_PROGRESS', 'CANCELLED']
},
IN_PROGRESS: {
value: 'IN_PROGRESS',
label: '进行中',
color: '#faad14',
allowedTransitions: ['DONE', 'TODO']
},
DONE: {
value: 'DONE',
label: '已完成',
color: '#52c41a',
allowedTransitions: ['TODO']
}
})
// 状态流转验证
function canTransition(from: string, to: string): boolean {
const fromInfo = getTaskStatusInfo(from)
return fromInfo.allowedTransitions?.includes(to) ?? false
}
3. API 错误码管理 🚨
typescript
const [ErrorCode, getErrorInfo] = constructEnum({
SUCCESS: 0,
VALIDATION_ERROR: {
value: 'VALIDATION_ERROR',
code: 400,
message: '参数验证失败',
retry: false
},
UNAUTHORIZED: {
value: 'UNAUTHORIZED',
code: 401,
message: '未授权访问',
retry: false
},
SERVER_ERROR: {
value: 'SERVER_ERROR',
code: 500,
message: '服务器内部错误',
retry: true
}
})
🎊 总结
constructEnum
通过巧妙的类型设计和实现,解决了传统 TypeScript 枚举在元数据管理、类型灵活性等方面的痛点。它不仅保持了类型安全,还提供了更加灵活和便捷的使用方式。💫
在实际项目中,我们使用 constructEnum
重构了大量的枚举定义,代码变得更加简洁和易维护。特别是在需要国际化、状态管理、表单配置等场景下,它的优势更加明显。🌟
希望这个工具函数能够帮助到更多的开发者,让 TypeScript 枚举的使用更加优雅和高效!🚀
📄 完整代码
typescript
// 枚举配置的类型定义
type EnumConfig = {
[K: string]: number | string | { value: string | number; label?: string; [key: string]: any }
}
// 枚举对象的类型定义
type EnumObject<T extends EnumConfig> = {
[K in keyof T]: T[K] extends string | number ? T[K] : T[K] extends { value: string } ? T[K]['value'] : never
}
// 获取配置中所有对象类型的并集
type ExtractObjectConfigs<T extends EnumConfig, P extends keyof EnumObject<T>> = {
[K in keyof T]: T[K] extends { value: string } ? T[P] : never
}[keyof T]
// 枚举信息获取函数的类型定义 - 修复版本
type EnumInfoGetter<T extends EnumConfig> = <P extends keyof EnumObject<T>>(enumValue: P) => ExtractObjectConfigs<T, P>
// 构造枚举函数的类型定义
function constructEnum<T extends EnumConfig>(config: T): [EnumObject<T>, EnumInfoGetter<T>] {
// 创建枚举对象
const enumObj = {} as EnumObject<T>
// 创建值到配置的映射
const valueToConfig = new Map<string, any>()
// 遍历配置,构建枚举对象和映射
for (const [key, value] of Object.entries(config)) {
if (typeof value !== 'object') {
enumObj[key as keyof T] = value as EnumObject<T>[keyof T]
valueToConfig.set(value.toString(), value)
} else if (value && typeof value === 'object' && 'value' in value) {
enumObj[key as keyof T] = value.value as EnumObject<T>[keyof T]
valueToConfig.set(value.value.toString(), value)
}
}
// 创建获取枚举信息的函数
const getEnumInfo: EnumInfoGetter<T> = <K extends keyof EnumObject<T>>(enumValue: K) => {
return valueToConfig.get(enumValue as any)
}
return [enumObj, getEnumInfo]
}
// 导出类型和函数
export type { EnumConfig, EnumObject, EnumInfoGetter }
export { constructEnum }
// 🌟 使用示例
const [UserRole, getUserRoleInfo] = constructEnum({
ADMIN: {
value: 'ADMIN',
label: '管理员',
permissions: ['read', 'write', 'delete'],
level: 1
},
EDITOR: {
value: 'EDITOR',
label: '编辑者',
permissions: ['read', 'write'],
level: 2
},
VIEWER: {
value: 'VIEWER',
label: '查看者',
permissions: ['read'],
level: 3
}
})
// 🚀 使用方式
console.log(UserRole.ADMIN) // 'ADMIN'
const adminInfo = getUserRoleInfo('ADMIN')
// 返回: { value: 'ADMIN', label: '管理员', permissions: [...], level: 1 }
🌟 如果这篇文章对你有帮助,欢迎点赞、收藏和分享!有任何问题或建议,欢迎在评论区交流讨论。💬