Skip to main content

封装自定义枚举工厂方法

说明

在大型应用中,我们经常需要定义一组相关的常量,管理常量项(如状态、类型等)是一个常见需求。为了更好地组织和使用这些常量,我们可以灵活运用 TypeScript 的类型系统,封装一个枚举工厂方法 enumxFactory,以提高代码的可维护性和可读性。

1. 封装枚举工厂方法的作用

  • 简化常量管理:

通过 enumxFactory,我们可以将相关的常量项集中管理,避免分散在代码中的硬编码字符串或数字,提高代码的可读性。

  • 类型安全:

TypeScript 提供的类型系统能够帮助我们在编译时捕获错误。使用 enumxFactory 可以确保在使用枚举项时,避免拼写错误和类型不匹配的问题。

  • 可扩展性:

如果需要添加额外的字段(如颜色、正则表达式等),我们只需更新 IEnumxItem 接口,而不需要更改工厂方法的其他部分。

2. enumxFactory 的实现

  • 参数: 接收一个对象,其中每个键对应一个枚举项,值为包含 id 和 label 等属性的对象。
  • 返回值: 返回一个 Enumx 实例,以及各个枚举项的 id 作为属性。
/src/utils/enumxFactory.ts
/**
* 导出枚举工厂方法
* 如果有额外的字段,可以添加到 IEnumxItem 中,请添加注释
*
* @example
*
* const ProductType = enumxFactory({
* TEXT: {id: 'text', label: '文本'},
* IMAGE: {id: 'image', label: '图片'}
* });
*
* @file enumxFactory.ts
*/

/**
* 枚举对象的接口
*
* @interface IEnumxItem
*/
type IEnumxItem = {
[key: string]: any;
} & {
/**
* 枚举值
*
* @type {string | number}
* @memberof IEnumxItem
*/
id: string | number;

/**
* 枚举对应的label
*
* @type {string}
* @memberof IEnumxItem
*/
label: string;

/**
* 枚举项对应的 alias
*
* @type {string}
*/
alias?: string;

/**
* 颜色
*
* @type {string}
* @memberof IEnumxItem
*/
color?: string;

/**
* 正则
*
* @type {RegExp}
* @memberof IEnumxItem
*/
reg?: RegExp;

/**
* 是否不可用
*
* @type {boolean}
*/
disabled?: boolean;
};

interface IEnumxItemOptions {
[key: string]: IEnumxItem;
}

export class Enumx<T extends IEnumxItemOptions> {
public itemList: (IEnumxItem & T[keyof T])[] = [];

constructor(options: IEnumxItemOptions) {
Object.keys(options).forEach(key => {
const item = options[key];
item.alias = key;
(this as any)[key] = item.id;
this.itemList.push(item as any);
});
}

/**
* alias 枚举,开发阶段类型推断用
*
* @type {keyof T}
* @memberof Enumx
*/
public aliasEnum!: keyof T;

/**
* id 枚举,开发阶段类型推断用
*
* @type {T[keyof T]['id']}
* @memberof Enumx
*/
public idsEnum!: T[keyof T]['id'];

/**
* 转数组
*
* @returns
* @memberof Enumx
*/
toArray() {
return this.itemList.slice().filter(item => !!localStorage.getItem('DataEnumsAll') || !item.disabled);
}

/**
* 根据 id/value 找枚举项
*
* @param {any} id
* @returns {IEnumxItem}
* @memberof Enumx
*/
getItemById(id: any): (IEnumxItem & T[keyof T]) | undefined {
return this.itemList.find(item => item.id === id) as (IEnumxItem & T[keyof T]) | undefined;
}

/**
* 根据 id/value 获取对应枚举项的label
*
* @param {any} id
* @returns {string}
* @memberof Enumx
*/
getLabelById(id: any): string {
const item = this.getItemById(id);
return item ? item.label : '';
}
}

/**
* 枚举工厂方法
*
* @export
* @template T
* @param {T} options
* @returns {(Enumx<T> & {[key in keyof T]: T[key]['id']})}
*/
export function enumxFactory<T extends IEnumxItemOptions>(options: T): Enumx<T> & {[key in keyof T]: T[key]['id']} {
return new Enumx(options) as Enumx<T> & {
[key in keyof T]: T[key]['id'];
};
}

3. enumxFactory 的使用

3.1 基本用法

const ProductType = enumxFactory({
TEXT: { id: 'text', label: '文本' },
IMAGE: { id: 'image', label: '图片' },
});

// 获取枚举项的 id
console.log(ProductType.TEXT); // 输出: 'text'

// 获取枚举项的标签
console.log(ProductType.getLabelById('image')); // 输出: '图片'

// 转换为数组
const itemList = ProductType.toArray();
console.log(itemList); // 输出: 包含所有可用枚举项的数组

3.2 在项目中规范使用

/src/utils/enumxFactory.ts 创建枚举工厂方法后,可以在项目中创建专门的枚举文件存放目录:

/src/enums/index.ts 导出所有枚举文件:

/src/enums/index.ts
export * from './tool';
export * from './log';
// ... 其他枚举文件

/src/enums/ 下创建各类型具体的枚举存放文件:

/src/enums/tool.ts
import { enumxFactory } from "../utils/enumxFactory";

/**
* 工具类型
*/
export const EToolType = enumxFactory({
TEXT: {id: 'text' as const, label: '文本类工具'},
TEXT_CONVERT: {id: 'textconvert' as const, label: '文本转换'},
TEXT_REPLACE: {id: 'textreplace' as const, label: '文本替换'},
TEXT_COUNT: {id: 'textcount' as const, label: '文本统计'},
TEXT_TRANSLATE: {id: 'texttranslate' as const, label: '生成变量名'},
IMAGE: {id: 'image' as const, label: '图像类工具'},
IMAGE_COMPRESS: {id: 'imagecompress' as const, label: '图像压缩'},
});

// ... 其他工具类枚举值

使用时,遇到类型声明,可以直接通过 typeof 关键字配合 idsEnum 的 id 枚举在开发阶段进行类型推断:

export const EDatasetImportStatus = enumxFactory({
TO_BE_IMPORT: {id: 0 as const, label: '导入中', color: RUNNING_COLOR},
IMPORTING: {id: 1 as const, label: '导入中', color: RUNNING_COLOR, filterable: true},
IMPORT_DONE: {id: 2 as const, label: '导入完成', color: SUCCESS_COLOR, filterable: true},
// ... 其他枚举值
});

export interface Xxx {
status: typeof EDatasetImportStatus.idsEnum;
// ... 其他字段
}