create-enum-es 1.0.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,82 +1,333 @@
1
+ # create-enum-es
2
+
3
+ > 零依赖、类型安全的前端枚举工具,为 TypeScript 项目提供完整的枚举管理方案。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install create-enum-es
1
9
  ```
2
- import { createEnum } from '../dist/emun-create.es.js';
3
10
 
4
- const enum1 = createEnum({
5
- On: [1, '开启'],
6
- Off: [0, '关闭'],
7
- })
11
+ ## 快速开始
8
12
 
9
- console.log(enum1.getVal('On')) // 1
10
- console.log(enum1.getValList('On', 'Off')) // [1, 0]
11
- console.log(enum1.getValList('On')) // [1]
12
- console.log(enum1.getValList()) // [1, 0]
13
+ ```ts
14
+ import createEnum from "create-enum-es";
13
15
 
16
+ const Status = createEnum({
17
+ ACTIVE: [1, "激活", { color: "green" }],
18
+ INACTIVE: [0, "未激活", { color: "gray" }],
19
+ PENDING: [2, "待审核", { color: "orange" }],
20
+ } as const);
14
21
 
15
- console.log(enum1.getValMap('On', 'Off')) // {On: 1, Off: 0}
16
- console.log(enum1.getValMap('Off')) // {Off: 0}
17
- console.log(enum1.getValMap()) // {On: 1, Off: 0}
22
+ Status.ACTIVE; // 1(直接属性访问)
23
+ Status.label("ACTIVE"); // '激活'
24
+ Status.label(1); // '激活'(通过 value label)
25
+ Status.extra("ACTIVE"); // { color: 'green' }
26
+ ```
18
27
 
28
+ > **重要:** 定义时需要在对象末尾加上 `as const` 以获得完整的类型推导。
19
29
 
20
- console.log(enum1.getName('On')) // 开启
21
- console.log(enum1.getNameByValue(enum1.Off)) // 关闭
22
- console.log(enum1.getName(enum1.On, { arguType: 'value' })) // 开启
30
+ ## API
23
31
 
32
+ ### `createEnum(enumMap)`
24
33
 
25
- console.log(enum1.getOptions())
26
- // [
27
- // {
28
- // "value": 1,
29
- // "label": "开启"
30
- // },
31
- // {
32
- // "value": 0,
33
- // "label": "关闭"
34
- // }
35
- // ]
36
- console.log(enum1.getOptions({ labelKey: 'name', valueKey: 'key' }))
37
- // [
38
- // {
39
- // "key": 1,
40
- // "name": "开启"
41
- // },
42
- // {
43
- // "key": 0,
44
- // "name": "关闭"
45
- // }
46
- // ]
47
- console.log(enum1.getOptions('On'))
48
- // [
49
- // {
50
- // "value": 1,
51
- // "label": "开启"
52
- // }
53
- // ]
54
- console.log(enum1.getOptions(enum1.On, { arguType: 'value' }))
55
- // [
56
- // {
57
- // "value": 1,
58
- // "label": "开启"
59
- // }
60
- // ]
61
- console.log(enum1.getOptionsByValues())
34
+ 创建一个冻结的枚举实例。参数格式:
35
+
36
+ ```ts
37
+ {
38
+ KEY: [value, label, extra?] // extra 可选
39
+ } as const
40
+ ```
41
+
42
+ ---
43
+
44
+ ### 属性访问
45
+
46
+ ```ts
47
+ Status.ACTIVE // 1
48
+ Status.INACTIVE // 0
49
+ ```
50
+
51
+ 直接通过 Key 获取 Value,享受完整的类型提示。
52
+
53
+ ---
54
+
55
+ ### `value(key)`
56
+
57
+ 通过 Key 获取 Value。
58
+
59
+ ```ts
60
+ Status.value("ACTIVE"); // 1
61
+ ```
62
+
63
+ ---
64
+
65
+ ### `values(...keys?)`
66
+
67
+ 获取多个枚举值。不传参返回所有值。
68
+
69
+ ```ts
70
+ Status.values(); // [1, 0, 2]
71
+ Status.values("ACTIVE", "PENDING"); // [1, 2]
72
+ ```
73
+
74
+ ---
75
+
76
+ ### `keys()`
77
+
78
+ 获取所有枚举 Key。
79
+
80
+ ```ts
81
+ Status.keys(); // ['ACTIVE', 'INACTIVE', 'PENDING']
82
+ ```
83
+
84
+ ---
85
+
86
+ ### `label(keyOrValue)`
87
+
88
+ 通过 Key 或 Value 获取 Label,支持双向查找。
89
+
90
+ ```ts
91
+ Status.label("ACTIVE"); // '激活'
92
+ Status.label(1); // '激活'
93
+ ```
94
+
95
+ ---
96
+
97
+ ### `extra(keyOrValue)`
98
+
99
+ 通过 Key 或 Value 获取附加数据。
100
+
101
+ ```ts
102
+ Status.extra("ACTIVE"); // { color: 'green' }
103
+ Status.extra(1); // { color: 'green' }
104
+ ```
105
+
106
+ ---
107
+
108
+ ### `keyOf(value)`
109
+
110
+ 通过 Value 反查 Key。
111
+
112
+ ```ts
113
+ Status.keyOf(1); // 'ACTIVE'
114
+ Status.keyOf(0); // 'INACTIVE'
115
+ ```
116
+
117
+ ---
118
+
119
+ ### `check(val, key)`
120
+
121
+ 检测一个值是否等于指定 Key 对应的枚举值。
122
+
123
+ ```ts
124
+ Status.check(1, "ACTIVE"); // true
125
+ Status.check(1, "INACTIVE"); // false
126
+ ```
127
+
128
+ ---
129
+
130
+ ### `has(val)`
131
+
132
+ 判断一个值是否属于该枚举(类型守卫)。
133
+
134
+ ```ts
135
+ Status.has(1); // true
136
+ Status.has(99); // false
137
+
138
+ // 类型守卫:
139
+ const val: unknown = 1;
140
+ if (Status.has(val)) {
141
+ // val 在此分支中被收窄为 TEValue<typeof Status>
142
+ }
143
+ ```
144
+
145
+ ---
146
+
147
+ ### `size`
148
+
149
+ 获取枚举项数量。
150
+
151
+ ```ts
152
+ Status.size; // 3
153
+ ```
154
+
155
+ ---
156
+
157
+ ### `options(...keys?)`
158
+
159
+ 获取枚举选项列表,适配 Select/Radio/Checkbox 等 UI 组件。不传参返回所有。
160
+
161
+ ```ts
162
+ Status.options();
62
163
  // [
63
- // {
64
- // "value": 1,
65
- // "label": "开启"
66
- // },
67
- // {
68
- // "value": 0,
69
- // "label": "关闭"
70
- // }
164
+ // { value: 1, label: '激活', extra: { color: 'green' } },
165
+ // { value: 0, label: '未激活', extra: { color: 'gray' } },
166
+ // { value: 2, label: '待审核', extra: { color: 'orange' } },
71
167
  // ]
72
- console.log(enum1.getOptionsByValues(enum1.On, { labelKey: 'name', valueKey: 'key' }))
168
+
169
+ Status.options("ACTIVE", "PENDING");
170
+ // 只返回指定项
171
+ ```
172
+
173
+ ---
174
+
175
+ ### `pick(...keys)`
176
+
177
+ 选取指定 Key,生成新的枚举实例。
178
+
179
+ ```ts
180
+ const ActiveOnly = Status.pick("ACTIVE", "PENDING");
181
+ ActiveOnly.ACTIVE; // 1
182
+ ActiveOnly.PENDING; // 2
183
+ // ActiveOnly.INACTIVE ← 类型错误,不存在
184
+ ```
185
+
186
+ ---
187
+
188
+ ### `omit(...keys)`
189
+
190
+ 排除指定 Key,生成新的枚举实例。
191
+
192
+ ```ts
193
+ const WithoutPending = Status.omit("PENDING");
194
+ WithoutPending.ACTIVE; // 1
195
+ WithoutPending.INACTIVE; // 0
196
+ // WithoutPending.PENDING ← 类型错误,不存在
197
+ ```
198
+
199
+ ---
200
+
201
+ ### `filter(predicate)`
202
+
203
+ 按条件过滤枚举项,返回满足条件的 options 数组。
204
+
205
+ ```ts
206
+ // 过滤掉 INACTIVE
207
+ Status.filter((value) => value !== 0);
73
208
  // [
74
- // {
75
- // "key": 1,
76
- // "name": "开启"
77
- // }
209
+ // { value: 1, label: '激活', extra: { color: 'green' } },
210
+ // { value: 2, label: '待审核', extra: { color: 'orange' } },
78
211
  // ]
79
212
 
80
- console.log(enum1.check(enum1.On, 'On')) // true
213
+ // 根据 extra 过滤
214
+ Status.filter((_v, _l, _k, extra) => extra?.color === "green");
215
+ ```
216
+
217
+ 回调参数:`(value, label, key, extra) => boolean`
218
+
219
+ ---
220
+
221
+ ### `forEach(callback)`
222
+
223
+ 遍历所有枚举项。
224
+
225
+ ```ts
226
+ Status.forEach((value, label, key, extra) => {
227
+ console.log(`${key}: ${value} - ${label}`, extra);
228
+ });
229
+ ```
230
+
231
+ ---
232
+
233
+ ### `for...of` 迭代
234
+
235
+ 枚举实例实现了 `Symbol.iterator`,支持 `for...of` 和展开运算符。
236
+
237
+ ```ts
238
+ for (const { key, value, label, extra } of Status) {
239
+ console.log(key, value, label, extra);
240
+ }
241
+
242
+ const items = [...Status]; // 展开为数组
81
243
  ```
82
244
 
245
+ ---
246
+
247
+ ### `toMap()`
248
+
249
+ 转为 `Map<value, label>`。
250
+
251
+ ```ts
252
+ Status.toMap();
253
+ // Map { 1 => '激活', 0 => '未激活', 2 => '待审核' }
254
+ ```
255
+
256
+ ---
257
+
258
+ ### `toRecord()`
259
+
260
+ 转为 `{ [value]: label }` 对象。
261
+
262
+ ```ts
263
+ Status.toRecord();
264
+ // { 1: '激活', 0: '未激活', 2: '待审核' }
265
+ ```
266
+
267
+ ---
268
+
269
+ ## TypeScript 类型支持
270
+
271
+ 所有 API 均提供完整的类型推导:
272
+
273
+ ```ts
274
+ const Status = createEnum({
275
+ ACTIVE: [1, "激活", { color: "green" }],
276
+ INACTIVE: [0, "未激活", { color: "gray" }],
277
+ } as const);
278
+
279
+ Status.ACTIVE; // 类型为 1(字面量)
280
+ Status.value("ACTIVE"); // 类型为 number
281
+ Status.label("ACTIVE"); // 类型为 string
282
+ Status.extra("ACTIVE"); // 类型为 { readonly color: "green" }
283
+
284
+ // pick/omit 返回类型正确收窄
285
+ const picked = Status.pick("ACTIVE");
286
+ picked.ACTIVE; // ✅
287
+ picked.INACTIVE; // ❌ 类型错误
288
+ ```
289
+
290
+ ## 典型场景
291
+
292
+ ### 配合 Select 组件
293
+
294
+ ```tsx
295
+ const Status = createEnum({ ... } as const);
296
+
297
+ // React / Vue
298
+ <Select options={Status.options()} />
299
+
300
+ // 只展示部分选项
301
+ <Select options={Status.options("ACTIVE", "PENDING")} />
302
+
303
+ // 动态过滤
304
+ <Select options={Status.filter((v) => v !== 0)} />
305
+ ```
306
+
307
+ ### 表格列渲染
308
+
309
+ ```tsx
310
+ const columns = [
311
+ {
312
+ title: "状态",
313
+ render: (val) => Status.label(val),
314
+ },
315
+ ];
316
+ ```
317
+
318
+ ### 条件判断
319
+
320
+ ```ts
321
+ if (Status.check(row.status, "ACTIVE")) {
322
+ // ...
323
+ }
324
+
325
+ // 或者
326
+ if (row.status === Status.ACTIVE) {
327
+ // ...
328
+ }
329
+ ```
330
+
331
+ ## License
332
+
333
+ ISC
@@ -1,241 +1 @@
1
- 'use strict';
2
-
3
- /**
4
- * 数据类型
5
- * @param {*} data
6
- * @param {String} type
7
- * @returns {Boolean}
8
- */
9
- function isType(data, type) {
10
- const dataType = Object.prototype.toString.call(data).slice(8, -1).toLowerCase();
11
- return type === dataType;
12
- }
13
- /**
14
- * 数据是否为空
15
- * @param {*} data
16
- * @returns
17
- */
18
- function isEmpty(data) {
19
- if (isType(data, "array") || isType(data, "string")) {
20
- return data.length === 0;
21
- }
22
- if (data instanceof Map || data instanceof Set) {
23
- return data.size === 0;
24
- }
25
- if (isType(data, "object")) {
26
- return Object.keys(data).length === 0;
27
- }
28
- return Boolean(data);
29
- }
30
- /**
31
- * 深度拷贝
32
- * @param {Object|Array} obj
33
- * @return {Object|Array}
34
- */
35
- function deepClone(obj) {
36
- let objClone = Array.isArray(obj) ? [] : {};
37
- if (obj && typeof obj === "object") {
38
- for (let key in obj) {
39
- if (obj.hasOwnProperty(key)) {
40
- //判断ojb子元素是否为对象,如果是,递归复制
41
- if (obj[key] && typeof obj[key] === "object") {
42
- objClone[key] = deepClone(obj[key]);
43
- }
44
- else {
45
- //如果不是,简单复制
46
- objClone[key] = obj[key];
47
- }
48
- }
49
- }
50
- }
51
- return objClone;
52
- }
53
-
54
- /**
55
- * 获取配置参数
56
- * @param {*} args
57
- * @returns
58
- */
59
- function getConfigParams(args) {
60
- let config = { labelKey: "label", valueKey: "value", arguType: "key" }; // 选项参数配置
61
- const lastArgu = args[args.length - 1];
62
- if (isType(lastArgu, "object")) {
63
- config = { ...config, ...lastArgu };
64
- args = args.slice(0, args.length - 1);
65
- }
66
- return [config, args];
67
- }
68
- /**
69
- * 判断枚举key列表是否有效
70
- * @param {*} args
71
- * @returns {Boolean}
72
- */
73
- function judgEnumKeys(keys) {
74
- return !isEmpty(keys) && keys.every((key) => typeof key === "string");
75
- }
76
- /**
77
- * 枚举类
78
- * @param {Object} enumMap 枚举对象
79
- * 枚举格式:
80
- * {
81
- * 枚举key1: [枚举值1,枚举描述1],
82
- * 枚举key2: [枚举值2,枚举描述2]
83
- * }
84
- */
85
- class Enum {
86
- __enumMap__;
87
- __enumValueMap__;
88
- __enumNameMap__;
89
- __valueNameMap__;
90
- /**
91
- * @param {Object} enumMap 枚举map
92
- */
93
- constructor(enumMap) {
94
- if (!isType(enumMap, "object")) {
95
- throw new TypeError("初始化参数值必须是一个object!");
96
- }
97
- this.#init(deepClone(enumMap));
98
- }
99
- /**
100
- * 初始化
101
- * @param {Object} enumMap 枚举对象
102
- * @private
103
- */
104
- #init(enumMap) {
105
- this.#setEnumMap(enumMap); //处理映射关系
106
- }
107
- /**
108
- * 设置枚举间的映射
109
- * @param {Object} enumMap
110
- * @private
111
- */
112
- #setEnumMap(enumMap) {
113
- const enumValueMap = {};
114
- const enumNameMap = {};
115
- const valueNameMap = {};
116
- Object.keys(enumMap).forEach((key) => {
117
- const item = enumMap[key];
118
- if (!Array.isArray(item)) {
119
- throw new TypeError("初始化参数对象字段的值必是一个array!");
120
- }
121
- enumValueMap[key] = item[0];
122
- enumNameMap[key] = item[1];
123
- valueNameMap[item[0]] = item[1];
124
- });
125
- this.__enumMap__ = Object.freeze(enumMap);
126
- this.__enumValueMap__ = Object.freeze(enumValueMap);
127
- this.__enumNameMap__ = Object.freeze(enumNameMap);
128
- this.__valueNameMap__ = Object.freeze(valueNameMap);
129
- }
130
- /**
131
- * 获取枚举值
132
- * @param {String} key 枚举KEY
133
- * @return {Number} 枚举值
134
- */
135
- getVal(key) {
136
- return this.__enumValueMap__[key];
137
- }
138
- /**
139
- * 获取多个枚举值
140
- * @param {Array} param 多个枚举KEY
141
- * @return {Array} {[枚举值]}
142
- */
143
- getValList(...args) {
144
- let keys = Object.keys(this.__enumMap__); // 不传递返回所有
145
- if (judgEnumKeys(args)) {
146
- keys = Array.from(args);
147
- }
148
- return keys.map((key) => this.getVal(key));
149
- }
150
- /**
151
- * 获取多个枚举值Map
152
- * @param {Array} param 多个枚举KEY,如果不传递则返回所有
153
- * @return {Object} {[枚举key]:枚举值}
154
- */
155
- getValMap(...args) {
156
- let keys = Object.keys(this.__enumMap__); // 不传递返回所有
157
- if (judgEnumKeys(args)) {
158
- keys = Array.from(args);
159
- }
160
- return keys.reduce((wrap, key) => {
161
- wrap[key] = this.getVal(key);
162
- return wrap;
163
- }, {});
164
- }
165
- getName(keyOrVal, _config) {
166
- const [config] = getConfigParams([_config]);
167
- if (config.arguType === "key") {
168
- return this.__enumNameMap__[keyOrVal];
169
- }
170
- else if (config.arguType === "value") {
171
- return this.getNameByValue(keyOrVal);
172
- }
173
- else {
174
- throw new TypeError("参数arguType的值类型不为 key|value !");
175
- }
176
- }
177
- /**
178
- * 通过枚举值获取枚举名称
179
- * @param {String|Nunber} val
180
- * @return {String|Null}
181
- */
182
- getNameByValue(val) {
183
- return this.__valueNameMap__[val];
184
- }
185
- getOptions(..._args) {
186
- const [config, args] = getConfigParams(_args);
187
- if (config.arguType === "key") {
188
- let keys = Object.keys(this.__enumMap__); // 不传递返回所有
189
- if (judgEnumKeys(args)) {
190
- keys = Array.from(args);
191
- }
192
- return keys.map((key) => {
193
- const value = this.getVal(key);
194
- const name = this.getName(key);
195
- return { [config.valueKey]: value, [config.labelKey]: name };
196
- });
197
- }
198
- else if (config.arguType === "value") {
199
- return this.getOptionsByValues(..._args);
200
- }
201
- else {
202
- throw new TypeError("参数arguType的值类型不为 key | value !");
203
- }
204
- }
205
- getOptionsByValues(..._args) {
206
- const [config, args] = getConfigParams(_args);
207
- let values = Object.values(this.__enumValueMap__); // 不传递返回所有
208
- if (!isEmpty(args)) {
209
- values = Array.from(args);
210
- }
211
- return values.map((value) => {
212
- const name = this.getNameByValue(value);
213
- return { [config.valueKey]: value, [config.labelKey]: name };
214
- });
215
- }
216
- /**
217
- * 检测字段类型
218
- * @param {Number} typeVal 类型
219
- * @param {String} typeKey 类型key
220
- * @return {Boolean}
221
- */
222
- check(typeVal, typeKey) {
223
- return this.getVal(typeKey) === typeVal;
224
- }
225
- }
226
- /**
227
- * 创建枚举
228
- * @param {Object} enumMap
229
- * @param {String} description
230
- * @returns
231
- */
232
- const createEnum = (enumMap) => {
233
- const enumInstance = new Enum(enumMap); // 返回实例
234
- const e = Object.create(enumInstance);
235
- for (const key in enumMap) {
236
- e[key] = enumMap[key]?.[0];
237
- }
238
- return Object.freeze(e);
239
- };
240
-
241
- exports.createEnum = createEnum;
1
+ "use strict";class e{__enumMap__;__enumLabelMap__;__enumExtraMap__;__valueKeyMap__;constructor(e){if(null===e||"object"!=typeof e||Array.isArray(e))throw new TypeError("初始化参数值必须是一个 object!");const t={},_={},r={},n=Object.keys(e);for(let a=0;a<n.length;a++){const s=n[a],u=e[s];if(!Array.isArray(u))throw new TypeError(`初始化参数对象字段 "${s}" 的值必须是一个 array!`);this[s]=u[0],t[s]=u[1],t[u[0]]=u[1],_[s]=u[2],_[u[0]]=u[2],r[u[0]]=s}this.__enumMap__=Object.freeze(e),this.__enumLabelMap__=Object.freeze(t),this.__enumExtraMap__=Object.freeze(_),this.__valueKeyMap__=Object.freeze(r)}keys(){return Object.keys(this.__enumMap__)}value(e){return this[e]}values(...e){return(e.length>0?e:this.keys()).map(e=>this.value(e))}options(...e){return(e.length>0?e:this.keys()).map(e=>({value:this.value(e),label:this.label(e),extra:this.extra(e)}))}label(e){return this.__enumLabelMap__[e]}extra(e){return this.__enumExtraMap__[e]}keyOf(e){return this.__valueKeyMap__[e]}check(e,t){return this.value(t)===e}has(e){return this.__valueKeyMap__.hasOwnProperty(e)}get size(){return Object.keys(this.__enumMap__).length}omit(...e){const _={},r=new Set(e),n=Object.keys(this.__enumMap__);for(let e=0;e<n.length;e++){const t=n[e];r.has(t)||(_[t]=this.__enumMap__[t])}return t(_)}pick(...e){const _={};for(let t=0;t<e.length;t++){const r=e[t];this.__enumMap__.hasOwnProperty(r)&&(_[r]=this.__enumMap__[r])}return t(_)}forEach(e){const t=Object.keys(this.__enumMap__);for(let _=0;_<t.length;_++){const r=t[_];e(this.value(r),this.__enumLabelMap__[r],r,this.__enumExtraMap__[r])}}toMap(){const e=new Map,t=Object.keys(this.__enumMap__);for(let _=0;_<t.length;_++){const r=t[_],n=this.__enumMap__[r];e.set(n[0],n[1])}return e}toRecord(){const e={},t=Object.keys(this.__enumMap__);for(let _=0;_<t.length;_++){const r=t[_],n=this.__enumMap__[r];e[n[0]]=n[1]}return e}filter(e){const t=[],_=Object.keys(this.__enumMap__);for(let r=0;r<_.length;r++){const n=_[r],a=this.value(n),s=this.__enumLabelMap__[n],u=this.__enumExtraMap__[n];e(a,s,n,u)&&t.push({value:a,label:s,extra:u})}return t}[Symbol.iterator](){const e=Object.keys(this.__enumMap__);let t=0;const _=this;return{next(){if(t<e.length){const r=e[t++];return{done:!1,value:{key:r,value:_.value(r),label:_.__enumLabelMap__[r],extra:_.__enumExtraMap__[r]}}}return{done:!0,value:void 0}}}}}const t=t=>{const _=new e(t);return Object.freeze(_)};module.exports=t;