ts-enum-next 1.0.1 → 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 +209 -42
- package/dist/index.d.ts +10 -7
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.zh-CN.md
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
# ts-enum-next
|
|
1
|
+
# 使用 ts-enum-next 优雅的管理 typeScript enum
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
在业务中,我们可能需要同时定义枚举值和枚举值说明,前者用来匹配,后者用来展示。
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
59
|
-
console.log(HttpStatus.
|
|
60
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
);
|
|
77
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
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
|
|
11
|
-
static fromValue<T extends Enum
|
|
12
|
-
static fromName<T extends Enum
|
|
13
|
-
static setOf<T extends Enum
|
|
14
|
-
static enumMap<
|
|
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():
|
|
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