ts-enum-next 1.0.0 → 1.0.2

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.zh-CN.md CHANGED
@@ -1,14 +1,8 @@
1
- # ts-enum-next
1
+ # 使用 ts-enum-next 优雅的管理 typeScript enum
2
2
 
3
- <b>English | <a href="./README.zh-CN.md">中文</a></b>
3
+ 在业务中,我们可能需要同时定义枚举值和枚举值说明,前者用来匹配,后者用来展示。
4
4
 
5
- - TypeScript 中下一代枚举的终极解决方案
6
-
7
- - 像在 java 中使用 enum 一样,在 typescript 中定义和使用 enum。
8
-
9
- ## 为什么?
10
-
11
- 在 typescript 中使用 enum 来定义数字枚举和字符串枚举是非常方便的, 但是在面对一些数字字典类型的枚举做定义时就有点力不从心, 比如我们定义一组状态时需要同时定义状态的名称, 我们可以就需要结合 typescript 中的枚举和映射对象来实现
5
+ typescript 中可以使用 ***枚举 + 映射对象***的方式实现。
12
6
 
13
7
  ```ts
14
8
  enum Status {
@@ -28,64 +22,237 @@ console.log(Status.PENDING); // 0
28
22
  console.log(StatusDescriptions[Status.PENDING]); // "等待处理"
29
23
  ```
30
24
 
31
- 在简单的项目中,这样维护“数据字典”是一个不错的方法, 但是在复杂的项目中,我们可能会需要维护大量的“数据字典”, 并且这些“数据字典” 可能需要一些继承一些“字典” 生成新的“字典”,或者业务中我们对一组枚举值中的不同描述展示不同的颜色或者执行不同的方法,这时 typescript 原生的 enum 就开始显得力不从心了
25
+ 或者使用对象字面量模拟枚举
26
+
27
+ ```ts
28
+ const Status = {
29
+ PENDING: {
30
+ value: 0,
31
+ description: "等待处理"
32
+ },
33
+ APPROVED: {
34
+ value: 1,
35
+ description: "已批准"
36
+ },
37
+ REJECTED: {
38
+ value: 2,
39
+ description: "已拒绝"
40
+ }
41
+ } as const;
32
42
 
33
- ts-enum-next 根据 java 中对于 enum 的定义和使用,提供了一套类似于 java 的 enum 定义和使用范式, 你可以实现基于 Class 的枚举定义和使用。
43
+ // 使用示例
44
+ console.log(Status.PENDING.value); // 0
45
+ console.log(Status.PENDING.description); // "等待处理"
46
+ ```
34
47
 
48
+ 在业务中,我们除了需要这样简单的枚举定义类型, 我们还会需要处理很多`数据字典`的业务场景。尤其是在以 Node 构建的服务端, 可能需要使用类的能力来定义这些枚举值, 以方便包含其他字段、方法、构造函数、描述元数据等能力。
35
49
 
36
- ## 使用
50
+ [ts-enum-next](url) 根据在 Java 中使用 enum 的场景,封装了一个抽象类 Enum 和一些辅助的工具类型, 来提供类似 Java 的枚举功能:
37
51
 
38
- ### 安装
52
+ * Java 形式的数据定义
53
+ * 丰富的枚举元数据
54
+ * 双向查找能力
55
+ * 枚举集合操作
56
+ * 自定义行为支持
39
57
 
40
- ```bash
41
- pnpm add ts-enum-next
42
- ```
58
+ ## ts-enum-next 的使用场景
59
+
60
+ ### 场景1: 基础枚举定义(带值和描述)
61
+
62
+ 定义枚举时, 同时添加: 枚举值、枚举名称、枚举描述元数据(可选)
43
63
 
44
- ### 定义数据字典
45
64
  ```ts
65
+ import { Enum } from 'ts-enum-next';
66
+
46
67
  class HttpStatus extends Enum<number> {
47
- static readonly OK = new HttpStatus(200, 'OK', '请求成功');
48
- static readonly BAD_REQUEST = new HttpStatus(400, 'BAD_REQUEST', '错误请求');
49
- static readonly NOT_FOUND = new HttpStatus(404, 'NOT_FOUND');
68
+ static readonly OK = new HttpStatus(200, "OK", "请求成功");
69
+ static readonly BAD_REQUEST = new HttpStatus(400, "BAD_REQUEST", "错误请求");
70
+ static readonly NOT_FOUND = new HttpStatus(404, "NOT_FOUND", "资源未找到");
50
71
  }
72
+
73
+
74
+ // 使用示例
75
+ console.log(HttpStatus.OK.value); // 200
76
+ console.log(HttpStatus.OK.name); // "OK"
77
+ console.log(HttpStatus.OK.description); // "请求成功"
51
78
  ```
79
+ #### 适用场景
52
80
 
53
- ### 使用数据字典
81
+ - 需要为枚举值附加元信息(如状态码描述)
82
+ - 需要根据数值快速查找对应的枚举实例
83
+ - 需要保持严格的类型安全
54
84
 
55
- * 获取字典项
85
+ ### 场景2: 枚举集合操作
56
86
 
57
87
  ```ts
58
- console.log(HttpStatus.OK.description); // "请求成功"
59
- console.log(HttpStatus.fromValue(404).name); // "NOT_FOUND"
60
- console.log(HttpStatus.fromName("BAD_REQUEST").value); // 400
88
+ // 获取所有枚举实例, 返回的是所有的枚举类
89
+ console.log(HttpStatus.values()); // [HttpStatus, HttpStatus, HttpStatus, ....]
90
+
91
+
92
+
93
+ // 通过值获取枚举实例
94
+ console.log(HttpStatus.fromValue(200)); // HttpStatus(200, "OK", "请求成功")
95
+
96
+ // 通过名称获取枚举实例
97
+ console.log(HttpStatus.fromName('OK')); // HttpStatus(200, "OK", "请求成功")
98
+
99
+
100
+
101
+ // 创建枚举集合
102
+ const errorStatus = HttpStatus.setOf(
103
+ HttpStatus.BAD_REQUEST,
104
+ HttpStatus.NOT_FOUND
105
+ );
106
+
107
+ console.log(errorStatus); // Set(2) {HttpStatus, HttpStatus}
108
+
109
+
110
+ // 检查包含关系
111
+ const currentStatus = HttpStatus.fromValue(400)
112
+
113
+ if (errorStatus.has(currentStatus))) {
114
+ console.error("当前是错误状态:", currentStatus.description);
115
+ }
61
116
  ```
62
117
 
63
- * 获取所有枚举值
118
+ **适用场景**:
119
+
120
+
121
+ - 需要分组管理枚举值(如所有错误状态)
122
+ - 需要检查枚举值是否属于某个特定集合
123
+ - 需要遍历枚举的所有可能值
124
+
125
+ ### 场景 3:枚举映射表
126
+
127
+ ```
128
+ // 创建枚举到字符串的映射
129
+ const statusMessages = HttpStatus.enumMap<string>({
130
+ [HttpStatus.OK]: "操作成功完成",
131
+ [HttpStatus.BAD_REQUEST]: "请检查您的请求参数",
132
+ [HttpStatus.NOT_FOUND]: "请求的资源不存在"
133
+ });
134
+
135
+ // 使用映射
136
+ console.log(statusMessages.get(HttpStatus.NOT_FOUND)); // “请求的资源不存在”
137
+ ```
138
+ **适用场景**:
139
+
140
+ - 需要为不同枚举值关联不同的数据
141
+ - 需要高效查找枚举对应的资源
142
+ - 需要类型安全的键值存储
143
+
144
+ ### 场景 4:自定义枚举方法
64
145
 
65
146
  ```ts
66
- const allStatuses = HttpStatus.values();
67
- console.log(allStatuses.map(s => s.name)); // ["OK", "BAD_REQUEST", "NOT_FOUND"]
147
+ class OrderStatus extends Enum<number> {
148
+ static readonly CREATED = new OrderStatus(0, "CREATED");
149
+ static readonly PAID = new OrderStatus(1, "PAID");
150
+ static readonly SHIPPED = new OrderStatus(2, "SHIPPED");
151
+
152
+ private constructor(
153
+ public readonly value: number,
154
+ public readonly name: string
155
+ ) {
156
+ super();
157
+ }
158
+
159
+ // 自定义方法
160
+ public canTransitionTo(target: OrderStatus): boolean {
161
+ const validTransitions = {
162
+ [OrderStatus.CREATED.value]: [OrderStatus.PAID.value],
163
+ [OrderStatus.PAID.value]: [OrderStatus.SHIPPED.value]
164
+ };
165
+ return validTransitions[this.value]?.includes(target.value) || false;
166
+ }
167
+ }
168
+
169
+ // 使用自定义方法
170
+ const current = OrderStatus.CREATED;
171
+ console.log(current.canTransitionTo(OrderStatus.PAID)); // true
172
+ console.log(current.canTransitionTo(OrderStatus.SHIPPED)); // false
68
173
  ```
69
174
 
70
- * 使用枚举集合
175
+ **适用场景**:
176
+
177
+ - 需要为枚举添加业务逻辑
178
+ - 需要实现状态机或工作流
179
+ - 需要封装枚举相关的复杂判断逻辑
180
+
181
+ ### 场景 5:枚举序列化/反序列化
71
182
 
72
183
  ```ts
73
- const errorStatuses = HttpStatus.setOf(
74
- HttpStatus.BAD_REQUEST,
75
- HttpStatus.NOT_FOUND
76
- );
77
- console.log(errorStatuses.has(HttpStatus.fromValue(400))); // true
184
+ class UserRole extends Enum<string> {
185
+ static readonly ADMIN = new UserRole("admin", "Administrator");
186
+ static readonly EDITOR = new UserRole("editor", "Content Editor");
187
+ static readonly VIEWER = new UserRole("viewer", "Read-only User");
188
+
189
+ private constructor(
190
+ public readonly value: string,
191
+ public readonly displayName: string
192
+ ) {
193
+ super();
194
+ }
195
+
196
+ // JSON序列化
197
+ public toJSON() {
198
+ return this.value;
199
+ }
200
+
201
+ // 从JSON反序列化
202
+ public static fromJSON(value: string): UserRole {
203
+ return UserRole.fromValue(value);
204
+ }
205
+ }
206
+
207
+ // 序列化示例
208
+ const role = UserRole.ADMIN;
209
+ const json = JSON.stringify(role); // ""admin""
210
+
211
+ // 反序列化示例
212
+ const parsedRole = UserRole.fromJSON(JSON.parse(json));
213
+ console.log(parsedRole === UserRole.ADMIN); // true
78
214
  ```
79
215
 
80
- * 使用枚举映射表
216
+ **适用场景**:
217
+
218
+ - 需要将枚举与JSON相互转换
219
+ - 需要处理API响应中的枚举值
220
+ - 需要保持前后端枚举值的一致性
221
+
222
+ ### 场景 6:获取原始枚举值类型
81
223
 
82
224
  ```ts
83
- const statusMessages = HttpStatus.enumMap({
84
- [HttpStatus.OK.value]: "操作成功",
85
- [HttpStatus.BAD_REQUEST.value]: "请求错误",
86
- "NOT_FOUND": "资源不存在" // 支持名称或值作为键
87
- });
88
- console.log(statusMessages.get(HttpStatus.NOT_FOUND)); // "资源不存在"
89
- ``
225
+ import { Enum, EnumValues } from 'ts-enum-next';
226
+
227
+ class HttpStatus extends Enum<200 | 400 | 404> {
228
+ static readonly OK = new HttpStatus(200, "OK", "请求成功");
229
+ static readonly BAD_REQUEST = new HttpStatus(400, "BAD_REQUEST", "错误请求");
230
+ static readonly NOT_FOUND = new HttpStatus(404, "NOT_FOUND", "资源未找到");
231
+ }
232
+
233
+ // 获取HttpStatus 的原始枚举值类型
234
+ export type HttpStatusEnum = EnumValues<typeof HttpStatus>;
235
+
236
+ // 使用原始枚举值类型再去定义其他类型
237
+ export type ResponseData<T> = {
238
+ message: string;
239
+ data: T;
240
+ code: HttpStatusEnum;
241
+ };
242
+ ```
243
+
244
+ **适用场景**:
245
+
246
+ - 需要获取原始枚举值类型
247
+ - 使用原始枚举值类型再去定义其他类型
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+
90
257
 
91
258
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  type EnumValueType = number | string;
2
- declare abstract class Enum<T extends EnumValueType> {
2
+ declare abstract class Enum<T extends string | number = string | number> {
3
3
  readonly value: T;
4
4
  readonly name: string;
5
5
  readonly description?: string | undefined;
@@ -7,13 +7,16 @@ declare abstract class Enum<T extends EnumValueType> {
7
7
  private static _valueMap;
8
8
  private static _nameMap;
9
9
  constructor(value: T, name: string, description?: string | undefined);
10
- static values<T extends Enum<any>>(this: typeof Enum): T[];
11
- static fromValue<T extends Enum<any>>(this: any, value: T['value']): T;
12
- static fromName<T extends Enum<any>>(this: typeof Enum, name: string): T;
13
- static setOf<T extends Enum<any>>(this: new () => T, ...items: T[]): Set<T>;
14
- static enumMap<T extends Enum<any>, V>(this: typeof Enum, map: Record<string | number, V>): Map<T, V>;
10
+ static values<T extends Enum>(): T[];
11
+ static fromValue<T extends Enum>(this: any, value: T['value']): T;
12
+ static fromName<T extends Enum>(this: any, name: string): T;
13
+ static setOf<T extends Enum>(this: any, ...items: T[]): Set<T>;
14
+ static enumMap<V>(this: any, map: Record<string | number, V>): Map<Enum, V>;
15
15
  toString(): string;
16
- valueOf(): T;
16
+ valueOf(): string;
17
17
  }
18
+ export type EnumValues<T> = {
19
+ [K in keyof T]: T[K] extends Enum<infer V> ? V : never;
20
+ }[keyof T];
18
21
  export { Enum };
19
22
  export type { EnumValueType };
package/dist/index.js CHANGED
@@ -54,7 +54,7 @@ class Enum {
54
54
  }
55
55
  // 重写valueOf方法
56
56
  valueOf() {
57
- return this.value;
57
+ return String(this.value);
58
58
  }
59
59
  }
60
60
  Enum._values = new Map();
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "ts-enum-next",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Ultimate Enum Enhancement for TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepublishOnly": "pnpm run build"
10
+ },
7
11
  "keywords": [
8
12
  "typescript",
9
13
  "enum",
@@ -12,12 +16,14 @@
12
16
  "enum-next",
13
17
  "java"
14
18
  ],
19
+ "files": [
20
+ "dist",
21
+ "README.md",
22
+ "README.zh-CN.md"
23
+ ],
15
24
  "author": "rico",
16
25
  "license": "ISC",
17
26
  "devDependencies": {
18
27
  "typescript": "^5.8.3"
19
- },
20
- "scripts": {
21
- "build": "tsc"
22
28
  }
23
- }
29
+ }
package/src/index.ts DELETED
@@ -1,78 +0,0 @@
1
- type EnumValueType = number | string;
2
-
3
- abstract class Enum<T extends EnumValueType> {
4
- private static _values: Map<typeof Enum, Enum<any>[]> = new Map();
5
- private static _valueMap: Map<typeof Enum, Map<EnumValueType, Enum<any>>> = new Map();
6
- private static _nameMap: Map<typeof Enum, Map<string, Enum<any>>> = new Map();
7
-
8
- constructor(
9
- public readonly value: T,
10
- public readonly name: string,
11
- public readonly description?: string
12
- ) {
13
- const enumClass = this.constructor as typeof Enum;
14
-
15
- // 初始化存储结构
16
- if (!Enum._values.has(enumClass)) {
17
- Enum._values.set(enumClass, []);
18
- Enum._valueMap.set(enumClass, new Map());
19
- Enum._nameMap.set(enumClass, new Map());
20
- }
21
-
22
- // 注册当前枚举实例
23
- Enum._values.get(enumClass)!.push(this);
24
- Enum._valueMap.get(enumClass)!.set(value, this);
25
- Enum._nameMap.get(enumClass)!.set(name, this);
26
- }
27
-
28
- // 获取所有枚举值
29
- static values<T extends Enum<any>>(this: typeof Enum): T[] {
30
- return (this._values.get(this) as T[]) || [];
31
- }
32
-
33
- // 通过值获取枚举实例
34
- static fromValue<T extends Enum<any>>(this: any, value: T['value']): T {
35
- const enumInstance = this._valueMap.get(this)?.get(value);
36
- if (!enumInstance) {
37
- console.error(`No enum value ${value} found`);
38
- }
39
- return enumInstance as T;
40
- }
41
-
42
- // 通过名称获取枚举实例
43
- static fromName<T extends Enum<any>>(this: typeof Enum, name: string): T {
44
- const enumInstance = this._nameMap.get(this)?.get(name);
45
- if (!enumInstance) {
46
- console.error(`No enum name ${name} found`);
47
- }
48
- return enumInstance as T;
49
- }
50
-
51
- // 创建枚举集合
52
- static setOf<T extends Enum<any>>(this: new () => T, ...items: T[]): Set<T> {
53
- return new Set(items);
54
- }
55
-
56
- // 创建枚举映射表
57
- static enumMap<T extends Enum<any>, V>(this: typeof Enum, map: Record<string | number, V>): Map<T, V> {
58
- const result = new Map<T, V>();
59
- for (const [key, value] of Object.entries(map)) {
60
- const enumKey = isNaN(Number(key)) ? (this.fromName(key) as T) : (this.fromValue(key as any) as T);
61
- result.set(enumKey, value);
62
- }
63
- return result;
64
- }
65
-
66
- // 重写toString方法
67
- toString(): string {
68
- return this.name;
69
- }
70
-
71
- // 重写valueOf方法
72
- valueOf(): T {
73
- return this.value;
74
- }
75
- }
76
-
77
- export { Enum };
78
- export type { EnumValueType };
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "esModuleInterop": true,
6
- "strict": true,
7
- "skipLibCheck": true,
8
- "rootDir": "./src",
9
- "outDir": "./dist",
10
- "declaration": true,
11
- "incremental": false,
12
- "baseUrl": "./",
13
- "moduleResolution": "node"
14
- },
15
- "include": ["src"]
16
- }