@quanxiaoxiao/datav 0.4.0 → 0.5.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.
Files changed (64) hide show
  1. package/README.md +680 -165
  2. package/dist/createArrayAccessor.d.ts +2 -0
  3. package/dist/createArrayAccessor.d.ts.map +1 -0
  4. package/dist/createArrayAccessor.js +38 -0
  5. package/dist/createArrayAccessor.js.map +1 -0
  6. package/dist/createDataAccessor.d.ts +2 -0
  7. package/dist/createDataAccessor.d.ts.map +1 -0
  8. package/dist/createDataAccessor.js +23 -0
  9. package/dist/createDataAccessor.js.map +1 -0
  10. package/dist/createDataTransformer.d.ts +14 -0
  11. package/dist/createDataTransformer.d.ts.map +1 -0
  12. package/dist/createDataTransformer.js +124 -0
  13. package/dist/createDataTransformer.js.map +1 -0
  14. package/dist/createPathAccessor.d.ts +2 -0
  15. package/dist/createPathAccessor.d.ts.map +1 -0
  16. package/dist/createPathAccessor.js +38 -0
  17. package/dist/createPathAccessor.js.map +1 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +5 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/parseDotPath.d.ts +2 -0
  23. package/dist/parseDotPath.d.ts.map +1 -0
  24. package/dist/parseDotPath.js +14 -0
  25. package/dist/parseDotPath.js.map +1 -0
  26. package/dist/parseValueByType.d.ts +11 -0
  27. package/dist/parseValueByType.d.ts.map +1 -0
  28. package/dist/parseValueByType.js +122 -0
  29. package/dist/parseValueByType.js.map +1 -0
  30. package/dist/utils.d.ts +3 -0
  31. package/dist/utils.d.ts.map +1 -0
  32. package/dist/utils.js +22 -0
  33. package/dist/utils.js.map +1 -0
  34. package/dist/validateExpressSchema.d.ts +7 -0
  35. package/dist/validateExpressSchema.d.ts.map +1 -0
  36. package/dist/validateExpressSchema.js +53 -0
  37. package/dist/validateExpressSchema.js.map +1 -0
  38. package/package.json +47 -8
  39. package/src/createArrayAccessor.test.ts +181 -0
  40. package/src/createArrayAccessor.ts +48 -0
  41. package/src/createDataAccessor.test.ts +220 -0
  42. package/src/createDataAccessor.ts +26 -0
  43. package/src/createDataTransformer.test.ts +847 -0
  44. package/src/createDataTransformer.ts +173 -0
  45. package/src/createPathAccessor.test.ts +217 -0
  46. package/src/createPathAccessor.ts +45 -0
  47. package/src/index.ts +11 -0
  48. package/src/parseDotPath.test.ts +132 -0
  49. package/src/parseDotPath.ts +13 -0
  50. package/src/parseValueByType.test.ts +342 -0
  51. package/src/parseValueByType.ts +165 -0
  52. package/src/utils.test.ts +85 -0
  53. package/src/utils.ts +22 -0
  54. package/src/validateExpressSchema.test.ts +295 -0
  55. package/src/validateExpressSchema.ts +62 -0
  56. package/.editorconfig +0 -13
  57. package/eslint.config.mjs +0 -89
  58. package/src/checkout.mjs +0 -131
  59. package/src/checkout.test.mjs +0 -144
  60. package/src/index.mjs +0 -7
  61. package/src/select/check.mjs +0 -63
  62. package/src/select/check.test.mjs +0 -76
  63. package/src/select/index.mjs +0 -117
  64. package/src/select/index.test.mjs +0 -1145
@@ -0,0 +1,342 @@
1
+ import * as assert from 'node:assert';
2
+ import { describe, it } from 'node:test';
3
+
4
+ import {
5
+ DATA_TYPE_ARRAY,
6
+ DATA_TYPE_BOOLEAN,
7
+ DATA_TYPE_INTEGER,
8
+ DATA_TYPE_JSON,
9
+ DATA_TYPE_NUMBER,
10
+ DATA_TYPE_OBJECT,
11
+ DATA_TYPE_STRING,
12
+ parseValueByType,
13
+ } from './parseValueByType.js';
14
+
15
+ describe('parseValueByType', () => {
16
+ describe('参数验证', () => {
17
+ it('应该在类型参数为空时抛出错误', () => {
18
+ assert.throws(() => parseValueByType('test', null as unknown as string), /data type is empty/);
19
+ assert.throws(() => parseValueByType('test', undefined as unknown as string), /data type is empty/);
20
+ });
21
+
22
+ it('应该在类型参数无效时抛出错误', () => {
23
+ assert.throws(() => parseValueByType('aaa', 'bbb' as unknown as string), /invalid data type/);
24
+ assert.throws(() => parseValueByType('test', 'invalid' as unknown as string), /invalid data type/);
25
+ });
26
+ });
27
+
28
+ describe('NULL 和 UNDEFINED 处理', () => {
29
+ const nullTestCases = [
30
+ { type: DATA_TYPE_STRING, expected: null, desc: 'string' },
31
+ { type: DATA_TYPE_NUMBER, expected: null, desc: 'number' },
32
+ { type: DATA_TYPE_INTEGER, expected: null, desc: 'integer' },
33
+ { type: DATA_TYPE_BOOLEAN, expected: null, desc: 'boolean' },
34
+ { type: DATA_TYPE_JSON, expected: null, desc: 'json' },
35
+ { type: DATA_TYPE_OBJECT, expected: null, desc: 'object' },
36
+ { type: DATA_TYPE_ARRAY, expected: [], desc: 'array' },
37
+ ];
38
+
39
+ nullTestCases.forEach(({ type, expected, desc }) => {
40
+ it(`应该将 null 转换为 ${JSON.stringify(expected)} (${desc} 类型)`, () => {
41
+ assert.deepStrictEqual(parseValueByType(null, type), expected);
42
+ });
43
+
44
+ it(`应该将 undefined 转换为 ${JSON.stringify(expected)} (${desc} 类型)`, () => {
45
+ assert.deepStrictEqual(parseValueByType(undefined, type), expected);
46
+ });
47
+ });
48
+ });
49
+
50
+ describe('STRING 类型转换', () => {
51
+ const testCases = [
52
+ { input: 'hello', expected: 'hello', desc: '保持字符串不变' },
53
+ { input: '', expected: '', desc: '保持空字符串不变' },
54
+ { input: ' 1', expected: ' 1', desc: '保留空格' },
55
+ { input: 123, expected: '123', desc: '数字转字符串' },
56
+ { input: 0, expected: '0', desc: '零转字符串' },
57
+ { input: 1, expected: '1', desc: '1转字符串' },
58
+ { input: true, expected: 'true', desc: 'true转字符串' },
59
+ { input: false, expected: 'false', desc: 'false转字符串' },
60
+ { input: null, expected: null, desc: 'null保持null' },
61
+ ];
62
+
63
+ testCases.forEach(({ input, expected, desc }) => {
64
+ it(`${desc}: ${JSON.stringify(input)} -> ${JSON.stringify(expected)}`, () => {
65
+ assert.strictEqual(parseValueByType(input, DATA_TYPE_STRING), expected);
66
+ });
67
+ });
68
+
69
+ it('应该使用数组的 toString 方法', () => {
70
+ assert.strictEqual(parseValueByType([1, 2, 3], DATA_TYPE_STRING), '1,2,3');
71
+ });
72
+
73
+ it('应该使用对象的 toString 方法', () => {
74
+ assert.strictEqual(parseValueByType({ name: 'cqq' }, DATA_TYPE_STRING), '[object Object]');
75
+ });
76
+
77
+ it('应该使用自定义 toString 方法', () => {
78
+ const obj = {
79
+ name: 'quan',
80
+ toString: () => 'cqq',
81
+ };
82
+ assert.strictEqual(parseValueByType(obj, DATA_TYPE_STRING), 'cqq');
83
+ });
84
+ });
85
+
86
+ describe('INTEGER 类型转换', () => {
87
+ describe('有效转换', () => {
88
+ const validCases = [
89
+ { input: '123', expected: 123 },
90
+ { input: '0', expected: 0 },
91
+ { input: '-456', expected: -456 },
92
+ { input: '1', expected: 1 },
93
+ { input: 42, expected: 42 },
94
+ { input: 1, expected: 1 },
95
+ ];
96
+
97
+ validCases.forEach(({ input, expected }) => {
98
+ it(`${JSON.stringify(input)} -> ${expected}`, () => {
99
+ assert.strictEqual(parseValueByType(input, DATA_TYPE_INTEGER), expected);
100
+ });
101
+ });
102
+ });
103
+
104
+ describe('浮点数取整', () => {
105
+ const floorCases = [
106
+ { input: 123.7, expected: 123 },
107
+ { input: '99.9', expected: 99 },
108
+ { input: -5.5, expected: -5 },
109
+ { input: 3.1, expected: 3 },
110
+ { input: '1.1', expected: 1 },
111
+ { input: '-3.1', expected: -3 },
112
+ ];
113
+
114
+ floorCases.forEach(({ input, expected }) => {
115
+ it(`${JSON.stringify(input)} -> ${expected}`, () => {
116
+ assert.strictEqual(parseValueByType(input, DATA_TYPE_INTEGER), expected);
117
+ });
118
+ });
119
+ });
120
+
121
+ describe('无效输入返回 null', () => {
122
+ const invalidCases = [
123
+ '', 'abc', '12abc', '1.2.3', '01', ' 1',
124
+ true, false, {}, [], null, NaN,
125
+ ];
126
+
127
+ invalidCases.forEach(input => {
128
+ it(`${JSON.stringify(input)} -> null`, () => {
129
+ assert.strictEqual(parseValueByType(input, DATA_TYPE_INTEGER), null);
130
+ });
131
+ });
132
+ });
133
+ });
134
+
135
+ describe('NUMBER 类型转换', () => {
136
+ describe('有效转换', () => {
137
+ const validCases = [
138
+ { input: '123', expected: 123 },
139
+ { input: '123.45', expected: 123.45 },
140
+ { input: '-67.89', expected: -67.89 },
141
+ { input: '0', expected: 0 },
142
+ { input: '-1', expected: -1 },
143
+ { input: '-1.5', expected: -1.5 },
144
+ { input: '-2.5', expected: -2.5 },
145
+ { input: '2.5', expected: 2.5 },
146
+ { input: 42, expected: 42 },
147
+ { input: 3.14, expected: 3.14 },
148
+ { input: 1, expected: 1 },
149
+ { input: 1e3, expected: 1000 },
150
+ ];
151
+
152
+ validCases.forEach(({ input, expected }) => {
153
+ it(`${JSON.stringify(input)} -> ${expected}`, () => {
154
+ assert.strictEqual(parseValueByType(input, DATA_TYPE_NUMBER), expected);
155
+ });
156
+ });
157
+ });
158
+
159
+ it('应该保持 NaN', () => {
160
+ assert.ok(Number.isNaN(parseValueByType(NaN, DATA_TYPE_NUMBER)));
161
+ });
162
+
163
+ it('应该处理负零', () => {
164
+ const result = parseValueByType(-0, DATA_TYPE_NUMBER);
165
+ assert.strictEqual(Object.is(result, -0), true);
166
+ assert.strictEqual(parseValueByType('-0', DATA_TYPE_NUMBER), null);
167
+ });
168
+
169
+ describe('无效输入返回 null', () => {
170
+ const invalidCases = [
171
+ '', 'a', '1a', '01', '2.5a', '2.5.', '2.5.8',
172
+ 'abc', '12abc', true, false, {}, [], null,
173
+ 'NaN', '1e3',
174
+ ];
175
+
176
+ invalidCases.forEach(input => {
177
+ it(`${JSON.stringify(input)} -> null`, () => {
178
+ assert.strictEqual(parseValueByType(input, DATA_TYPE_NUMBER), null);
179
+ });
180
+ });
181
+
182
+ it('应该处理 Infinity', () => {
183
+ assert.strictEqual(parseValueByType('Infinity', DATA_TYPE_NUMBER), Infinity);
184
+ assert.strictEqual(parseValueByType('-Infinity', DATA_TYPE_NUMBER), -Infinity);
185
+ });
186
+ });
187
+ });
188
+
189
+ describe('BOOLEAN 类型转换', () => {
190
+ const testCases = [
191
+ { input: 'true', expected: true, desc: '字符串 "true"' },
192
+ { input: 'false', expected: false, desc: '字符串 "false"' },
193
+ { input: true, expected: true, desc: '布尔值 true' },
194
+ { input: false, expected: false, desc: '布尔值 false' },
195
+ ];
196
+
197
+ testCases.forEach(({ input, expected, desc }) => {
198
+ it(`应该转换${desc}`, () => {
199
+ assert.strictEqual(parseValueByType(input, DATA_TYPE_BOOLEAN), expected);
200
+ });
201
+ });
202
+
203
+ describe('无效输入返回 null', () => {
204
+ const invalidCases = [
205
+ '', ' false', 'false ', ' true', 'true ',
206
+ '0', '1', 'TRUE', 'FALSE', 0, 1, null,
207
+ ];
208
+
209
+ invalidCases.forEach(input => {
210
+ it(`${JSON.stringify(input)} -> null`, () => {
211
+ assert.strictEqual(parseValueByType(input, DATA_TYPE_BOOLEAN), null);
212
+ });
213
+ });
214
+ });
215
+ });
216
+
217
+ describe('JSON 类型转换', () => {
218
+ const validCases = [
219
+ { input: '{"name":"test"}', expected: { name: 'test' } },
220
+ { input: '[1,2,3]', expected: [1, 2, 3] },
221
+ { input: '{"arr":[1,2,3]}', expected: { arr: [1, 2, 3] } },
222
+ { input: '{"nested":{"a":1}}', expected: { nested: { a: 1 } } },
223
+ { input: '[]', expected: [] },
224
+ { input: '{}', expected: {} },
225
+ ];
226
+
227
+ validCases.forEach(({ input, expected }) => {
228
+ it(`${input} -> ${JSON.stringify(expected)}`, () => {
229
+ assert.deepStrictEqual(parseValueByType(input, DATA_TYPE_JSON), expected);
230
+ });
231
+ });
232
+
233
+ it('应该处理嵌套结构', () => {
234
+ const nested = '{"user":{"name":"test","age":20},"items":[1,2,3]}';
235
+ assert.deepStrictEqual(
236
+ parseValueByType(nested, DATA_TYPE_JSON),
237
+ { user: { name: 'test', age: 20 }, items: [1, 2, 3] },
238
+ );
239
+ });
240
+
241
+ describe('无效输入返回 null', () => {
242
+ const invalidCases = [
243
+ 'invalid', '{invalid}', '{fail}', "'1'",
244
+ 'aa', 123, 2, null,
245
+ ];
246
+
247
+ invalidCases.forEach(input => {
248
+ it(`${JSON.stringify(input)} -> null`, () => {
249
+ assert.strictEqual(parseValueByType(input, DATA_TYPE_JSON), null);
250
+ });
251
+ });
252
+ });
253
+ });
254
+
255
+ describe('OBJECT 类型转换', () => {
256
+ describe('有效对象解析', () => {
257
+ it('应该解析 JSON 对象字符串', () => {
258
+ assert.deepStrictEqual(
259
+ parseValueByType('{"name":"test","age":20}', DATA_TYPE_OBJECT),
260
+ { name: 'test', age: 20 },
261
+ );
262
+ assert.deepStrictEqual(
263
+ parseValueByType('{"name":"cqq"}', DATA_TYPE_OBJECT),
264
+ { name: 'cqq' },
265
+ );
266
+ });
267
+
268
+ it('应该保持对象不变', () => {
269
+ const obj = { name: 'test' };
270
+ assert.strictEqual(parseValueByType(obj, DATA_TYPE_OBJECT), obj);
271
+
272
+ const obj2 = { name: 'cqq' };
273
+ assert.deepStrictEqual(parseValueByType(obj2, DATA_TYPE_OBJECT), { name: 'cqq' });
274
+ });
275
+
276
+ it('应该解析空对象', () => {
277
+ assert.deepStrictEqual(parseValueByType('{}', DATA_TYPE_OBJECT), {});
278
+ assert.deepStrictEqual(parseValueByType({}, DATA_TYPE_OBJECT), {});
279
+ });
280
+ });
281
+
282
+ describe('无效输入返回 null', () => {
283
+ const invalidCases = [
284
+ 'invalid', '{invalid}',
285
+ 123, [], true, false, null,
286
+ ];
287
+
288
+ invalidCases.forEach(input => {
289
+ it(`${JSON.stringify(input)} -> null`, () => {
290
+ assert.strictEqual(parseValueByType(input, DATA_TYPE_OBJECT), null);
291
+ });
292
+ });
293
+ });
294
+ });
295
+
296
+ describe('ARRAY 类型转换', () => {
297
+ const validCases = [
298
+ { input: '[1,2,3]', expected: [1, 2, 3] },
299
+ { input: '["a","b"]', expected: ['a', 'b'] },
300
+ { input: '[{"name":"test"}]', expected: [{ name: 'test' }] },
301
+ { input: '[]', expected: [] },
302
+ ];
303
+
304
+ validCases.forEach(({ input, expected }) => {
305
+ it(`${input} -> ${JSON.stringify(expected)}`, () => {
306
+ assert.deepStrictEqual(parseValueByType(input, DATA_TYPE_ARRAY), expected);
307
+ });
308
+ });
309
+
310
+ describe('无效输入返回 null', () => {
311
+ const invalidCases = [
312
+ 'invalid', '[invalid', '1,2,3',
313
+ 123, {}, true, false, null,
314
+ ];
315
+
316
+ invalidCases.forEach(input => {
317
+ it(`${JSON.stringify(input)} -> []`, () => {
318
+ assert.deepStrictEqual(parseValueByType(input, DATA_TYPE_ARRAY), []);
319
+ });
320
+ });
321
+ });
322
+ });
323
+
324
+ describe('边界情况', () => {
325
+ it('应该处理大数字', () => {
326
+ const bigNum = 9007199254740991;
327
+ assert.strictEqual(parseValueByType(bigNum, DATA_TYPE_NUMBER), bigNum);
328
+ assert.strictEqual(parseValueByType(String(bigNum), DATA_TYPE_NUMBER), bigNum);
329
+ });
330
+
331
+ it('应该处理特殊字符串', () => {
332
+ assert.strictEqual(parseValueByType('NaN', DATA_TYPE_NUMBER), null);
333
+ assert.strictEqual(parseValueByType('Infinity', DATA_TYPE_NUMBER), Infinity);
334
+ assert.strictEqual(parseValueByType('-Infinity', DATA_TYPE_NUMBER), -Infinity);
335
+ });
336
+
337
+ it('应该处理科学计数法数字', () => {
338
+ assert.strictEqual(parseValueByType(1e10, DATA_TYPE_NUMBER), 10000000000);
339
+ assert.strictEqual(parseValueByType('1e5', DATA_TYPE_NUMBER), null);
340
+ });
341
+ });
342
+ });
@@ -0,0 +1,165 @@
1
+ import { isPlainObject } from './utils.js';
2
+
3
+ const DATA_TYPE_NUMBER = 'number';
4
+ const DATA_TYPE_STRING = 'string';
5
+ const DATA_TYPE_BOOLEAN = 'boolean';
6
+ const DATA_TYPE_JSON = 'json';
7
+ const DATA_TYPE_ARRAY = 'array';
8
+ const DATA_TYPE_OBJECT = 'object';
9
+ const DATA_TYPE_INTEGER = 'integer';
10
+
11
+ type DataType =
12
+ | typeof DATA_TYPE_NUMBER
13
+ | typeof DATA_TYPE_STRING
14
+ | typeof DATA_TYPE_BOOLEAN
15
+ | typeof DATA_TYPE_JSON
16
+ | typeof DATA_TYPE_ARRAY
17
+ | typeof DATA_TYPE_OBJECT
18
+ | typeof DATA_TYPE_INTEGER;
19
+
20
+ type ValueTransformer = (value: unknown) => unknown;
21
+
22
+ const typeTransformers: Record<DataType, ValueTransformer> = {
23
+ [DATA_TYPE_STRING]: (value) => {
24
+ if (typeof value === 'string') {
25
+ return value;
26
+ }
27
+ const strVal = value as { toString?: () => string };
28
+ return strVal.toString ? strVal.toString() : JSON.stringify(value);
29
+ },
30
+
31
+ [DATA_TYPE_INTEGER]: (value) => {
32
+ if (value === '' || value == null) {
33
+ return null;
34
+ }
35
+
36
+ const valueType = typeof value;
37
+ if (valueType !== 'number' && valueType !== 'string') {
38
+ return null;
39
+ }
40
+
41
+ const number = Number(value);
42
+ if (Number.isNaN(number)) {
43
+ return null;
44
+ }
45
+
46
+ if (String(number) !== String(value)) {
47
+ return null;
48
+ }
49
+
50
+ return parseInt(number.toString(), 10);
51
+ },
52
+
53
+ [DATA_TYPE_NUMBER]: (value) => {
54
+ if (value === '' || value == null) {
55
+ return null;
56
+ }
57
+
58
+ const number = Number(value);
59
+ if (Number.isNaN(number)) {
60
+ return null;
61
+ }
62
+
63
+ if (String(number) !== String(value)) {
64
+ return null;
65
+ }
66
+
67
+ return number;
68
+ },
69
+
70
+ [DATA_TYPE_BOOLEAN]: (value) => {
71
+ if (value !== 'false' && value !== 'true') {
72
+ return null;
73
+ }
74
+ return value === 'true';
75
+ },
76
+
77
+ [DATA_TYPE_JSON]: (value) => {
78
+ try {
79
+ return JSON.parse(value as string);
80
+ } catch {
81
+ return null;
82
+ }
83
+ },
84
+
85
+ [DATA_TYPE_OBJECT]: (value) => {
86
+ try {
87
+ const parsed = JSON.parse(value as string);
88
+
89
+ if (Array.isArray(parsed) || typeof parsed !== 'object') {
90
+ return null;
91
+ }
92
+
93
+ return parsed;
94
+ } catch {
95
+ return null;
96
+ }
97
+ },
98
+
99
+ [DATA_TYPE_ARRAY]: (value) => {
100
+ try {
101
+ const parsed = JSON.parse(value as string);
102
+ return Array.isArray(parsed) ? parsed : [];
103
+ } catch {
104
+ return [];
105
+ }
106
+ },
107
+ };
108
+
109
+ // 类型名称映射表(用于 typeof 检查)
110
+ const typeNameMap: Record<DataType, string> = {
111
+ [DATA_TYPE_NUMBER]: 'number',
112
+ [DATA_TYPE_STRING]: 'string',
113
+ [DATA_TYPE_INTEGER]: 'integer',
114
+ [DATA_TYPE_BOOLEAN]: 'boolean',
115
+ [DATA_TYPE_JSON]: 'object',
116
+ [DATA_TYPE_ARRAY]: 'object',
117
+ [DATA_TYPE_OBJECT]: 'object',
118
+ };
119
+
120
+ export function parseValueByType(value: unknown, type: DataType): unknown {
121
+ if (type == null) {
122
+ throw new Error('data type is empty');
123
+ }
124
+
125
+ if (!Object.hasOwnProperty.call(typeTransformers, type)) {
126
+ throw new Error(`\`${type}\` is an invalid data type`);
127
+ }
128
+
129
+ if (value == null) {
130
+ return type === DATA_TYPE_ARRAY ? [] : null;
131
+ }
132
+
133
+ const valueType = typeof value;
134
+
135
+ if (valueType !== 'string') {
136
+ if (type === DATA_TYPE_INTEGER || type === DATA_TYPE_STRING) {
137
+ return typeTransformers[type](value);
138
+ }
139
+
140
+ if (valueType === typeNameMap[type]) {
141
+ if (type === DATA_TYPE_ARRAY) {
142
+ return Array.isArray(value) ? value : [];
143
+ }
144
+ if (type === DATA_TYPE_OBJECT) {
145
+ return isPlainObject(value) ? value : null;
146
+ }
147
+ return value;
148
+ }
149
+
150
+ return type === DATA_TYPE_ARRAY ? [] : null;
151
+ }
152
+
153
+ return typeTransformers[type](value);
154
+ }
155
+
156
+ export {
157
+ DATA_TYPE_ARRAY,
158
+ DATA_TYPE_BOOLEAN,
159
+ DATA_TYPE_INTEGER,
160
+ DATA_TYPE_JSON,
161
+ DATA_TYPE_NUMBER,
162
+ DATA_TYPE_OBJECT,
163
+ DATA_TYPE_STRING,
164
+ type DataType,
165
+ };
@@ -0,0 +1,85 @@
1
+ import assert from 'node:assert';
2
+ import { describe, it } from 'node:test';
3
+
4
+ import { isEmpty, isPlainObject } from './utils.js';
5
+
6
+ describe('isPlainObject', () => {
7
+ it('应该返回 true 对于普通对象', () => {
8
+ assert.strictEqual(isPlainObject({}), true);
9
+ assert.strictEqual(isPlainObject({ name: 'test' }), true);
10
+ assert.strictEqual(isPlainObject({ a: 1, b: 2 }), true);
11
+ });
12
+
13
+ it('应该返回 false 对于非对象类型', () => {
14
+ assert.strictEqual(isPlainObject(null), false);
15
+ assert.strictEqual(isPlainObject(undefined), false);
16
+ assert.strictEqual(isPlainObject(123), false);
17
+ assert.strictEqual(isPlainObject('string'), false);
18
+ assert.strictEqual(isPlainObject(true), false);
19
+ assert.strictEqual(isPlainObject(0), false);
20
+ assert.strictEqual(isPlainObject(''), false);
21
+ });
22
+
23
+ it('应该返回 false 对于数组', () => {
24
+ assert.strictEqual(isPlainObject([]), false);
25
+ assert.strictEqual(isPlainObject([1, 2, 3]), false);
26
+ assert.strictEqual(isPlainObject(['a', 'b']), false);
27
+ });
28
+
29
+ it('应该返回 false 对于函数', () => {
30
+ assert.strictEqual(isPlainObject(function () {}), false);
31
+ assert.strictEqual(isPlainObject(() => {}), false);
32
+ });
33
+
34
+ it('应该返回 false 对于 Date 对象', () => {
35
+ assert.strictEqual(isPlainObject(new Date()), false);
36
+ });
37
+
38
+ it('应该返回 false 对于 RegExp 对象', () => {
39
+ assert.strictEqual(isPlainObject(/test/), false);
40
+ });
41
+ });
42
+
43
+ describe('isEmpty', () => {
44
+ it('应该返回 true 对于 null 和 undefined', () => {
45
+ assert.strictEqual(isEmpty(null), true);
46
+ assert.strictEqual(isEmpty(undefined), true);
47
+ });
48
+
49
+ it('应该返回 true 对于空数组', () => {
50
+ assert.strictEqual(isEmpty([]), true);
51
+ });
52
+
53
+ it('应该返回 false 对于非空数组', () => {
54
+ assert.strictEqual(isEmpty([1]), false);
55
+ assert.strictEqual(isEmpty(['a', 'b']), false);
56
+ });
57
+
58
+ it('应该返回 true 对于空对象', () => {
59
+ assert.strictEqual(isEmpty({}), true);
60
+ });
61
+
62
+ it('应该返回 false 对于非空对象', () => {
63
+ assert.strictEqual(isEmpty({ name: 'test' }), false);
64
+ assert.strictEqual(isEmpty({ a: 1, b: 2 }), false);
65
+ });
66
+
67
+ it('应该返回 false 对于空字符串', () => {
68
+ assert.strictEqual(isEmpty(''), false);
69
+ });
70
+
71
+ it('应该返回 false 对于非空字符串', () => {
72
+ assert.strictEqual(isEmpty('test'), false);
73
+ assert.strictEqual(isEmpty('a'), false);
74
+ });
75
+
76
+ it('应该返回 false 对于数字', () => {
77
+ assert.strictEqual(isEmpty(0), false);
78
+ assert.strictEqual(isEmpty(123), false);
79
+ });
80
+
81
+ it('应该返回 false 对于布尔值', () => {
82
+ assert.strictEqual(isEmpty(true), false);
83
+ assert.strictEqual(isEmpty(false), false);
84
+ });
85
+ });
package/src/utils.ts ADDED
@@ -0,0 +1,22 @@
1
+ export function isPlainObject(value: unknown): boolean {
2
+ if (typeof value !== 'object' || value === null) {
3
+ return false;
4
+ }
5
+ if (Array.isArray(value)) {
6
+ return false;
7
+ }
8
+ return Object.prototype.toString.call(value) === '[object Object]';
9
+ }
10
+
11
+ export function isEmpty(value: unknown): boolean {
12
+ if (value == null) {
13
+ return true;
14
+ }
15
+ if (Array.isArray(value)) {
16
+ return value.length === 0;
17
+ }
18
+ if (typeof value === 'object') {
19
+ return Object.keys(value).length === 0;
20
+ }
21
+ return false;
22
+ }