🚀 告别传统枚举的痛点!TypeScript 中的 constructEnum 让你的代码更优雅 ✨

🎯 前言

在日常开发中,我们经常需要定义各种枚举类型来表示状态、类型等常量值。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 类型推导。🎉

⭐ 核心特性

  1. 统一的配置对象:支持简单值和复杂对象混合定义 🔗
  2. 完整的类型推导:保持 TypeScript 类型安全 🛡️
  3. 元数据获取:提供便捷的信息获取方法 📊
  4. 灵活的值类型:支持字符串、数字等多种类型 🔢

🔧 实现解析

🎨 类型系统设计

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 }

🌟 如果这篇文章对你有帮助,欢迎点赞、收藏和分享!有任何问题或建议,欢迎在评论区交流讨论。💬

相关推荐
摇滚侠5 分钟前
JavaScript 浮点数计算精度错误示例
开发语言·javascript·ecmascript
xw524 分钟前
Trae安装指定版本的插件
前端·trae
天蓝色的鱼鱼33 分钟前
JavaScript垃圾回收:你不知道的内存管理秘密
javascript·面试
默默地离开42 分钟前
前端开发中的 Mock 实践与接口联调技巧
前端·后端·设计模式
南岸月明43 分钟前
做副业,稳住心态,不靠鸡汤!我的实操经验之路
前端
嘗_1 小时前
暑期前端训练day7——有关vue-diff算法的思考
前端·vue.js·算法
MediaTea1 小时前
Python 库手册:html.parser HTML 解析模块
开发语言·前端·python·html
杨荧1 小时前
基于爬虫技术的电影数据可视化系统 Python+Django+Vue.js
开发语言·前端·vue.js·后端·爬虫·python·信息可视化
BD_Marathon1 小时前
IDEA中创建Maven Web项目
前端·maven·intellij-idea
waillyer1 小时前
taro跳转路由取值
前端·javascript·taro