枚举管理的最佳实践:枚举 + 数据源 + Map 的统一编码风格

在前端开发中,我们经常遇到枚举值需要多处复用的情况,比如:

  • 用作接口查询参数(如账户类型、交易状态等)
  • 渲染下拉选择框
  • 表格或详情页中显示对应文本

如果每次都手动书写 ifswitch 或维护多份结构,很容易导致维护成本高、逻辑重复且容易出错。本文介绍一种结构化的编码风格,通过统一的数据源构建 枚举 + 列表 + Map,提高代码的可维护性、可读性和可扩展性


代码示例:交易流水类型

下面是一个典型的枚举结构示例:

ini 复制代码
/**
 * 交易类型枚举
 */
export enum TRANSACTION_TYPE {
  /** 全部 */
  ALL = '',
  /** 支出 */
  EXPENSE = 0,
  /** 收入 */
  INCOME = 1,
}

该枚举常用于接口查询参数、路由跳转等。但单独的枚举值并不能满足组件渲染和文案展示的需求,因此我们通过统一的数据源进行扩展。


构建统一的数据源

我们使用一个数组来作为"单一数据源":

css 复制代码
const TRANSACTION_TYPE_SOURCE: { value: TRANSACTION_TYPE; label: string }[] = [  { value: TRANSACTION_TYPE.INCOME, label: '收入' },  { value: TRANSACTION_TYPE.EXPENSE, label: '支出' },];

优势:

  • 集中定义
  • 结构扁平,易于维护
  • 可直接用于组件绑定

构建下拉选项和映射 Map

在实际项目中,这个枚举值可能会用于:

  • 下拉框渲染(需要 label + value 结构)
  • 表格/详情中的文案展示(需要通过 Map 映射)

于是我们进一步封装:

ini 复制代码
export const TRANSACTION_TYPE_LIST = [
  { value: TRANSACTION_TYPE.ALL, label: '全部' },
  ...TRANSACTION_TYPE_SOURCE,
];

export const TRANSACTION_TYPE_MAP = new Map(
  TRANSACTION_TYPE_SOURCE.map(item => [item.value, item.label])
);

这样:

  • TRANSACTION_TYPE_LIST 可用于 <a-select><el-select> 等组件
  • TRANSACTION_TYPE_MAP 可用于表格、标签、详情等位置的文字渲染

可选的安全获取函数

为了更安全地获取 label,推荐加一个辅助函数:

typescript 复制代码
export function getTransactionTypeLabel(type: TRANSACTION_TYPE | ''): string {
  if (type === TRANSACTION_TYPE.ALL) return '全部';
  return TRANSACTION_TYPE_MAP.get(type) || '--';
}

Bonus:提炼通用工具函数

如果你项目中存在多个类似的枚举结构,重复手写 listmap 结构不仅效率低,而且容易遗漏或写错。可以封装一个通用工具函数 createEnumHelper,标准化整个流程。

通用工具函数封装

建议新建一个工具文件:

arduino 复制代码
src/utils/enum-helper.ts

内容如下:

ts 复制代码
/**
 * 通用枚举工具函数:用于创建枚举数据的统一管理接口
 * @template T - 枚举值的类型
 * @param items - 枚举项的原始数据,包含 value 和 label
 * @param options - 可选参数:`includeAll` 用于是否包含 "全部" 选项,`allLabel` 用于定义 "全部" 选项的标签
 * @returns 返回一个对象,包括 `list`(下拉列表)、`map`(映射表)、`getLabel`(获取label的函数)
 */
export function createEnumHelper<T>(
	items: { value: T; label: string }[],
	options?: { includeAll?: boolean; allLabel?: string },
) {
	// 解构出 options,设置默认值
	const { includeAll = false, allLabel = '全部' } = options || {};

	// 如果需要包含"全部"选项,将其放在列表最前面
	const list = includeAll
		? [{ value: '' as any as T, label: allLabel }, ...items] // 添加 '全部' 选项
		: [...items]; // 如果不需要"全部",直接返回原数据

	// 创建枚举值与标签的映射关系
	const map = new Map<T, string>(items.map(item => [item.value, item.label]));

	/**
	 * 根据 value 获取对应的 label
	 * @param value - 枚举值,支持传入空字符串(对应 "全部")
	 * @returns 返回对应的 label,若无匹配返回 '--'
	 */
	function getLabel(value: T): string {
		// 如果是空字符串且包含 "全部",返回 "全部" 标签
		if (value === '' && includeAll) return allLabel;
		// 如果 Map 中有该值,则返回对应的 label;否则返回默认值 '--'
		return map.get(value) || '--';
	}

	// 返回包含 list、map 和 getLabel 的对象
	return {
		list, // 用于下拉框的列表数据
		map, // 用于映射的 Map
		getLabel, // 获取 label 的函数
	};
}

使用方式示例

在任意业务模块中使用它非常简单:

ini 复制代码
// enums/transaction-type.ts
import { createEnumHelper } from '@/utils/enum-helper';

export enum TRANSACTION_TYPE {
  ALL = '',
  EXPENSE = 0,
  INCOME = 1,
}

const TRANSACTION_TYPE_SOURCE = [
  { value: TRANSACTION_TYPE.INCOME, label: '收入' },
  { value: TRANSACTION_TYPE.EXPENSE, label: '支出' },
];

// 调用工具函数生成枚举列表、Map 和 label 获取函数
export const {
  list: TRANSACTION_TYPE_LIST,
  map: TRANSACTION_TYPE_MAP,
  getLabel: getTransactionTypeLabel,
} = createEnumHelper(TRANSACTION_TYPE_SOURCE, { includeAll: true });

使用场景示例

1. 用于下拉框:

ini 复制代码
<a-select
  v-model:value="form.transactionType"
  :options="TRANSACTION_TYPE_LIST"
  placeholder="请选择类型"
/>

2. 用于表格渲染:

javascript 复制代码
{
  title: '交易类型',
  dataIndex: 'transactionType',
  customRender: ({ text }) => getTransactionTypeLabel(text),
}

总结

这种编码风格具备以下优点:

优点 描述
统一性 所有枚举值统一来源
可扩展 新增类型只需维护一处
类型安全 枚举提供强类型保障
解耦 不依赖 UI 或具体实现,纯数据驱动

对于中大型系统,建议将这套方式纳入团队开发规范,配合统一的 enum-helper.ts 工具文件进行集中管理,能显著提升代码质量和开发效率。

如果你还希望扩展功能(如多语言支持、枚举权限控制等),这套结构也能非常轻松地适配与升级。

相关推荐
打野赵怀真17 分钟前
React Hooks 的优势和使用场景
前端·javascript
似水流年QC23 分钟前
什么是Lodash
javascript·lodash
知心宝贝27 分钟前
【Nest.js 通关秘籍 - 基础篇】带你轻松掌握后端开发
前端·javascript·架构
小叶爱吃鱼27 分钟前
python-各种文件(txt,xls,csv,sql,二进制文件)读写操作、文件类型转换、数据分析代码讲解
前端·javascript·python·学习
小钰能吃三碗饭28 分钟前
第八篇:【React 性能调优】从优化实践到自动化性能监控
前端·javascript·react.js
海底火旺31 分钟前
90% JS开发者不知道的属性陷阱:你的writable为什么失效了?
前端·javascript·深度学习
敲代码的玉米C34 分钟前
Babel实战指南:从基础概念到高效开发
前端·javascript·babel
不要额外加糖39 分钟前
CSS手残党救星:motion-v 助你写出流畅丝滑的动画
前端·javascript·vue.js
Monly2143 分钟前
Uniapp:确认框
开发语言·javascript·uni-app