← 返回导航

字典/枚举类型封装

业务类型 · 实战

场景说明

业务开发中字典/枚举无处不在:订单状态、用户角色、性别、审核状态等。传统的 TS enum 有诸多痛点(反向映射、无法直接遍历、不是真正的运行时对象),而用 as const 定义字典数组或对象,再通过 typeof + keyof 推导 value 和 label 的联合类型,既能保留运行时数据,又能获得完整的类型安全。配合根据 value 查 label 的工具函数,是前端字典管理的最佳实践。

完整代码


      
// 字典/枚举类型封装:as const 对象 + typeof 推导联合类型
// 运行时可遍历,编译期类型安全,替代传统 enum

// 1. 字典项基础类型
interface DictItem<V = string | number> {
  value: V;
  label: string;
  color?: string;
  disabled?: boolean;
}

// 2. 从 as const 字典数组推导 Value 联合类型
type DictValue<T extends readonly DictItem[]> = T[number]['value'];

// 3. 从 as const 字典数组推导 Label 联合类型
type DictLabel<T extends readonly DictItem[]> = T[number]['label'];

// 4. 示例:订单状态字典
const ORDER_STATUS = [
  { value: 0, label: '待支付', color: '#f59e0b' },
  { value: 1, label: '已支付', color: '#10b981' },
  { value: 2, label: '已发货', color: '#3b82f6' },
  { value: 3, label: '已完成', color: '#6366f1' },
  { value: 4, label: '已取消', color: '#ef4444' },
] as const;

// 5. 从字典推导出的类型
type OrderStatusValue = DictValue<typeof ORDER_STATUS>;  // 0 | 1 | 2 | 3 | 4
type OrderStatusLabel = DictLabel<typeof ORDER_STATUS>;  // '待支付' | '已支付' | ...

// 6. 对象形式的字典(按 value 映射)
const USER_ROLE = {
  admin: { value: 'admin', label: '管理员', color: '#ef4444' },
  editor: { value: 'editor', label: '编辑', color: '#f59e0b' },
  viewer: { value: 'viewer', label: '访客', color: '#6b7280' },
} as const;

type UserRoleKey = keyof typeof USER_ROLE;        // 'admin' | 'editor' | 'viewer'
type UserRoleValue = typeof USER_ROLE[keyof typeof USER_ROLE]['value'];  // 'admin' | 'editor' | 'viewer'

// 7. 根据 value 查 label 的工具函数
function getLabelByValue<T extends readonly DictItem[]>(
  dict: T,
  value: DictValue<T>,
): string | undefined {
  const item = dict.find(d => d.value === value);
  return item?.label;
}

// 8. 泛型工具:获取字典项类型
type DictItemType<T extends readonly DictItem[]> = T[number];

// 使用示例
const status: OrderStatusValue = 1;
const label = getLabelByValue(ORDER_STATUS, status);  // '已支付'

// 遍历字典(运行时可用)
const options = ORDER_STATUS.map(item => ({
  value: item.value,
  label: item.label,
}));

关键点解析

使用示例与类型推导


      
// value 传错会被 TS 拦截
// getLabelByValue(ORDER_STATUS, 99);  // 错误:99 不在 0 | 1 | 2 | 3 | 4 中

// 变量受联合类型约束
let status: OrderStatusValue = 0;
status = 3;  // OK
// status = 99;  // 错误

// 字典项类型推导
type OrderStatusItem = DictItemType<typeof ORDER_STATUS>;
// => { readonly value: 0; readonly label: '待支付'; readonly color: '#f59e0b'; } | ...

// 业务接口中使用字典值类型
interface Order {
  id: number;
  status: OrderStatusValue;  // 只能是 0-4
  role: UserRoleValue;        // 只能是 admin/editor/viewer
}

// 生成 select 选项
function toSelectOptions<T extends readonly DictItem[]>(dict: T) {
  return dict.map(item => ({
    value: item.value,
    label: item.label,
    disabled: item.disabled,
  }));
}
const opts = toSelectOptions(ORDER_STATUS);
在 Playground 中尝试