@jiakun-zhao/utils 1.4.2 → 1.4.4
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/LICENSE +21 -0
- package/README.md +0 -2
- package/dist/index.d.mts +620 -0
- package/dist/index.mjs +558 -1
- package/llms.txt +596 -0
- package/package.json +10 -8
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
//#region src/addons/assert.ts
|
|
2
|
+
/**
|
|
3
|
+
* 断言函数,当条件为 false 时抛出错误
|
|
4
|
+
* @param condition - 断言条件
|
|
5
|
+
* @param message - 错误信息
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* assert(true, '不会抛出错误') // 正常执行
|
|
9
|
+
* assert(false, '会抛出错误') // 抛出 Error: 会抛出错误
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
2
12
|
function assert(condition, message) {
|
|
3
13
|
if (!condition) throw new Error(message);
|
|
4
14
|
}
|
|
5
15
|
|
|
6
16
|
//#endregion
|
|
7
17
|
//#region src/addons/deep-equal.ts
|
|
18
|
+
/**
|
|
19
|
+
* 深度比较两个值是否相等
|
|
20
|
+
* 支持 Array、Object、RegExp、Date 等类型的比较
|
|
21
|
+
* @param a - 第一个值
|
|
22
|
+
* @param b - 第二个值
|
|
23
|
+
* @returns 是否深度相等
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* isDeepEqual(1, 1) // true
|
|
27
|
+
* isDeepEqual({}, {}) // true
|
|
28
|
+
* isDeepEqual('foo', 'foo') // true
|
|
29
|
+
* isDeepEqual([1, 2, 3], [1, 2, 3]) // true
|
|
30
|
+
* isDeepEqual(isDeepEqual, isDeepEqual) // true
|
|
31
|
+
* isDeepEqual(/foo/, /foo/) // true
|
|
32
|
+
* isDeepEqual(null, null) // true
|
|
33
|
+
* isDeepEqual(Number.NaN, Number.NaN) // true
|
|
34
|
+
* isDeepEqual([], []) // true
|
|
35
|
+
* isDeepEqual([{ a: 1 }, [{ b: { c: [1] } }]], [{ a: 1 }, [{ b: { c: [1] } }]]) // true
|
|
36
|
+
* isDeepEqual(1, '1') // false
|
|
37
|
+
* isDeepEqual(null, undefined) // false
|
|
38
|
+
* isDeepEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 5] }) // false
|
|
39
|
+
* isDeepEqual(/foo/i, /bar/g) // false
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
8
42
|
function isDeepEqual(a, b) {
|
|
9
43
|
if (a === b) return true;
|
|
10
44
|
const constructor = a?.constructor;
|
|
@@ -22,6 +56,17 @@ function isDeepEqual(a, b) {
|
|
|
22
56
|
|
|
23
57
|
//#endregion
|
|
24
58
|
//#region src/addons/http-status-code.ts
|
|
59
|
+
/**
|
|
60
|
+
* HTTP 状态码及其对应的描述信息
|
|
61
|
+
* 包含了所有标准的 HTTP 状态码
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* status[200] // 'OK'
|
|
65
|
+
* status[404] // 'Not Found'
|
|
66
|
+
* status[500] // 'Internal Server Error'
|
|
67
|
+
* status[418] // "I'm a Teapot"
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
25
70
|
const status = {
|
|
26
71
|
100: "Continue",
|
|
27
72
|
101: "Switching Protocols",
|
|
@@ -87,12 +132,53 @@ const status = {
|
|
|
87
132
|
510: "Not Extended",
|
|
88
133
|
511: "Network Authentication Required"
|
|
89
134
|
};
|
|
135
|
+
/**
|
|
136
|
+
* 根据 HTTP 状态码获取对应的描述信息
|
|
137
|
+
* @param code - HTTP 状态码
|
|
138
|
+
* @returns 状态码描述
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* getHttpStatusMessage(200) // 'OK'
|
|
142
|
+
* getHttpStatusMessage(404) // 'Not Found'
|
|
143
|
+
* getHttpStatusMessage(500) // 'Internal Server Error'
|
|
144
|
+
* getHttpStatusMessage(418) // "I'm a Teapot"
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
90
147
|
function getHttpStatusMessage(code) {
|
|
91
148
|
return status[code];
|
|
92
149
|
}
|
|
93
150
|
|
|
94
151
|
//#endregion
|
|
95
152
|
//#region src/addons/natural-compare.ts
|
|
153
|
+
/**
|
|
154
|
+
* 自然排序比较函数,按自然语言顺序比较字符串或数字
|
|
155
|
+
* 类似文件名排序,"file2" < "file10"
|
|
156
|
+
* @param a - 第一个值
|
|
157
|
+
* @param b - 第二个值
|
|
158
|
+
* @returns 比较结果:-1 表示 a < b,0 表示 a = b,1 表示 a > b
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* naturalCompare(1, 2) // -1
|
|
162
|
+
* naturalCompare(2, 1) // 1
|
|
163
|
+
* naturalCompare(1, 1) // 0
|
|
164
|
+
*
|
|
165
|
+
* naturalCompare('a', 'b') // -1
|
|
166
|
+
* naturalCompare('b', 'a') // 1
|
|
167
|
+
* naturalCompare('a', 'a') // 0
|
|
168
|
+
*
|
|
169
|
+
* // 数字字符串按数值大小排序,而不是字典序
|
|
170
|
+
* naturalCompare('file1', 'file2') // -1
|
|
171
|
+
* naturalCompare('file2', 'file10') // -1 (2 < 10 自然顺序)
|
|
172
|
+
* naturalCompare('file10', 'file2') // 1
|
|
173
|
+
*
|
|
174
|
+
* // 用于数组排序
|
|
175
|
+
* ['file10.txt', 'file2.txt', 'file1.txt'].sort(naturalCompare) // ['file1.txt', 'file2.txt', 'file10.txt']
|
|
176
|
+
*
|
|
177
|
+
* // 版本号比较
|
|
178
|
+
* naturalCompare('v1.0.1', 'v1.0.10') // -1
|
|
179
|
+
* naturalCompare('v1.0.10', 'v1.0.2') // 1
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
96
182
|
function naturalCompare(a, b) {
|
|
97
183
|
var i, codeA, codeB = 1, posA = 0, posB = 0, alphabet = String.alphabet;
|
|
98
184
|
function getCode(str, pos, code) {
|
|
@@ -118,13 +204,84 @@ function naturalCompare(a, b) {
|
|
|
118
204
|
|
|
119
205
|
//#endregion
|
|
120
206
|
//#region src/base/array.ts
|
|
207
|
+
/**
|
|
208
|
+
* 判断值是否为数组
|
|
209
|
+
* @param val - 要判断的值
|
|
210
|
+
* @returns 是否为数组
|
|
211
|
+
* @example
|
|
212
|
+
* ```ts
|
|
213
|
+
* isArray([]) // true
|
|
214
|
+
* isArray([1, 2, 3]) // true
|
|
215
|
+
* isArray('') // false
|
|
216
|
+
* isArray(null) // false
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
121
219
|
const isArray = (val) => Array.isArray(val);
|
|
220
|
+
/**
|
|
221
|
+
* 将值转换为数组,如果已经是数组则返回原数组
|
|
222
|
+
* @param val - 要转换的值
|
|
223
|
+
* @returns 数组
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* toArray(1) // [1]
|
|
227
|
+
* toArray([1, 2, 3]) // [1, 2, 3]
|
|
228
|
+
* toArray('hello') // ['hello']
|
|
229
|
+
* toArray(null) // [null]
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
122
232
|
const toArray = (val) => isArray(val) ? val : [val];
|
|
233
|
+
/**
|
|
234
|
+
* 数组去重
|
|
235
|
+
* @param arr - 输入数组
|
|
236
|
+
* @returns 去重后的数组
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* uniq([1, 2, 2, 3, 3, 3]) // [1, 2, 3]
|
|
240
|
+
* uniq(['a', 'b', 'a', 'c']) // ['a', 'b', 'c']
|
|
241
|
+
* uniq([]) // []
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
123
244
|
const uniq = (arr) => [...new Set(arr)];
|
|
245
|
+
/**
|
|
246
|
+
* 获取数组的单个元素,如果数组只有一个元素则返回该元素,否则返回 null
|
|
247
|
+
* @param arr - 输入数组
|
|
248
|
+
* @returns 单个元素或 null
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* singleOrNull([1]) // 1
|
|
252
|
+
* singleOrNull(['only']) // 'only'
|
|
253
|
+
* singleOrNull([]) // null
|
|
254
|
+
* singleOrNull([1, 2, 3]) // null
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
124
257
|
const singleOrNull = (arr) => arr.length === 1 ? arr[0] : null;
|
|
258
|
+
/**
|
|
259
|
+
* 创建指定长度的数组
|
|
260
|
+
* @param len - 数组长度
|
|
261
|
+
* @param mapFn - 映射函数,可选
|
|
262
|
+
* @returns 创建的数组
|
|
263
|
+
* @example
|
|
264
|
+
* ```ts
|
|
265
|
+
* createArray(3) // [0, 1, 2]
|
|
266
|
+
* createArray(3, (i) => i * 2) // [0, 2, 4]
|
|
267
|
+
* createArray(5, (i) => `item-${i}`) // ['item-0', 'item-1', 'item-2', 'item-3', 'item-4']
|
|
268
|
+
* createArray(0) // []
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
125
271
|
function createArray(len, mapFn) {
|
|
126
272
|
return Array.from({ length: len }, (_, index) => mapFn?.(index) ?? index);
|
|
127
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* 随机打乱数组(会修改原数组)
|
|
276
|
+
* @param array - 要打乱的数组
|
|
277
|
+
* @returns 打乱后的数组
|
|
278
|
+
* @example
|
|
279
|
+
* ```ts
|
|
280
|
+
* const arr = [1, 2, 3, 4, 5]
|
|
281
|
+
* shuffle(arr) // [3, 1, 5, 2, 4] (随机顺序)
|
|
282
|
+
* // ⚠️ 原数组也会被修改
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
128
285
|
function shuffle(array) {
|
|
129
286
|
for (let i = array.length - 1; i > 0; i--) {
|
|
130
287
|
const j = Math.floor(Math.random() * (i + 1));
|
|
@@ -132,6 +289,11 @@ function shuffle(array) {
|
|
|
132
289
|
}
|
|
133
290
|
return array;
|
|
134
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* 生成数字范围序列
|
|
294
|
+
* @param args - 参数
|
|
295
|
+
* @returns 数字数组
|
|
296
|
+
*/
|
|
135
297
|
function range(...args) {
|
|
136
298
|
if (args.length === 1) args.unshift(0);
|
|
137
299
|
const [start, stop, step = 1] = args;
|
|
@@ -142,46 +304,352 @@ function range(...args) {
|
|
|
142
304
|
|
|
143
305
|
//#endregion
|
|
144
306
|
//#region src/base/boolean.ts
|
|
307
|
+
/**
|
|
308
|
+
* 判断值是否为布尔类型
|
|
309
|
+
* @param val - 要判断的值
|
|
310
|
+
* @returns 是否为布尔值
|
|
311
|
+
* @example
|
|
312
|
+
* ```ts
|
|
313
|
+
* isBoolean(true) // true
|
|
314
|
+
* isBoolean(false) // true
|
|
315
|
+
* isBoolean('true') // false
|
|
316
|
+
* isBoolean(1) // false
|
|
317
|
+
* isBoolean(null) // false
|
|
318
|
+
* ```
|
|
319
|
+
*/
|
|
145
320
|
const isBoolean = (val) => typeof val === "boolean";
|
|
146
321
|
|
|
147
322
|
//#endregion
|
|
148
323
|
//#region src/base/string.ts
|
|
324
|
+
/**
|
|
325
|
+
* 获取值的字符串表示形式
|
|
326
|
+
* @param val - 输入值
|
|
327
|
+
* @returns 字符串表示
|
|
328
|
+
* @example
|
|
329
|
+
* ```ts
|
|
330
|
+
* toString({}) // '[object Object]'
|
|
331
|
+
* toString([]) // '[object Array]'
|
|
332
|
+
* toString(null) // '[object Null]'
|
|
333
|
+
* toString(undefined) // '[object Undefined]'
|
|
334
|
+
* toString(123) // '[object Number]'
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
149
337
|
const toString = (val) => Object.prototype.toString.call(val);
|
|
338
|
+
/**
|
|
339
|
+
* 判断值是否为字符串
|
|
340
|
+
* @param val - 要判断的值
|
|
341
|
+
* @returns 是否为字符串
|
|
342
|
+
* @example
|
|
343
|
+
* ```ts
|
|
344
|
+
* isString('hello') // true
|
|
345
|
+
* isString('') // true
|
|
346
|
+
* isString(123) // false
|
|
347
|
+
* isString(null) // false
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
150
350
|
const isString = (val) => typeof val === "string";
|
|
151
|
-
|
|
351
|
+
/**
|
|
352
|
+
* 将反斜杠转换为正斜杠
|
|
353
|
+
* @param str - 输入字符串
|
|
354
|
+
* @returns 转换后的字符串
|
|
355
|
+
* @example
|
|
356
|
+
* ```ts
|
|
357
|
+
* slash('C:\\Users\\test') // 'C:/Users/test'
|
|
358
|
+
* slash('path\\to\\file') // 'path/to/file'
|
|
359
|
+
* slash('path/to/file') // 'path/to/file'
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
const slash = (str) => str.replace(/\\\\/g, "/");
|
|
363
|
+
/**
|
|
364
|
+
* 确保字符串以指定前缀开头
|
|
365
|
+
* @param str - 输入字符串
|
|
366
|
+
* @param prefix - 前缀
|
|
367
|
+
* @returns 处理后的字符串
|
|
368
|
+
* @example
|
|
369
|
+
* ```ts
|
|
370
|
+
* ensurePrefix('example.com', 'https://') // 'https://example.com'
|
|
371
|
+
* ensurePrefix('https://example.com', 'https://') // 'https://example.com'
|
|
372
|
+
* ensurePrefix('path', '/') // '/path'
|
|
373
|
+
* ```
|
|
374
|
+
*/
|
|
152
375
|
const ensurePrefix = (str, prefix) => str.startsWith(prefix) ? str : prefix + str;
|
|
376
|
+
/**
|
|
377
|
+
* 确保字符串以指定后缀结尾
|
|
378
|
+
* @param str - 输入字符串
|
|
379
|
+
* @param suffix - 后缀
|
|
380
|
+
* @returns 处理后的字符串
|
|
381
|
+
* @example
|
|
382
|
+
* ```ts
|
|
383
|
+
* ensureSuffix('file', '.json') // 'file.json'
|
|
384
|
+
* ensureSuffix('file.json', '.json') // 'file.json'
|
|
385
|
+
* ensureSuffix('path', '/') // 'path/'
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
153
388
|
const ensureSuffix = (str, suffix) => str.endsWith(suffix) ? str : str + suffix;
|
|
389
|
+
/**
|
|
390
|
+
* 将字符串首字母大写,其余字母小写
|
|
391
|
+
* @param str - 输入字符串
|
|
392
|
+
* @returns 处理后的字符串
|
|
393
|
+
* @example
|
|
394
|
+
* ```ts
|
|
395
|
+
* capitalize('hello') // 'Hello'
|
|
396
|
+
* capitalize('HELLO') // 'Hello'
|
|
397
|
+
* capitalize('hELLO') // 'Hello'
|
|
398
|
+
* capitalize('hello world') // 'Hello world'
|
|
399
|
+
* // ⚠️ 空字符串会返回 'undefined'
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
154
402
|
const capitalize = (str) => str[0].toUpperCase() + str.slice(1).toLowerCase();
|
|
155
403
|
|
|
156
404
|
//#endregion
|
|
157
405
|
//#region src/base/date.ts
|
|
406
|
+
/**
|
|
407
|
+
* 判断值是否为 Date 对象
|
|
408
|
+
* @param val - 要判断的值
|
|
409
|
+
* @returns 是否为 Date 对象
|
|
410
|
+
* @example
|
|
411
|
+
* ```ts
|
|
412
|
+
* isDate(new Date()) // true
|
|
413
|
+
* isDate(new Date('2024-01-01')) // true
|
|
414
|
+
* isDate('2024-01-01') // false
|
|
415
|
+
* isDate(null) // false
|
|
416
|
+
* ```
|
|
417
|
+
*/
|
|
158
418
|
const isDate = (val) => toString(val) === "[object Date]";
|
|
419
|
+
/**
|
|
420
|
+
* 获取当前时间戳(毫秒)
|
|
421
|
+
* @returns 时间戳
|
|
422
|
+
* @example
|
|
423
|
+
* ```ts
|
|
424
|
+
* timestamp() // 1705401600000
|
|
425
|
+
* ```
|
|
426
|
+
*/
|
|
159
427
|
const timestamp = () => +Date.now();
|
|
428
|
+
/**
|
|
429
|
+
* 判断日期是否为今天
|
|
430
|
+
* @param date - 要判断的日期
|
|
431
|
+
* @returns 是否为今天
|
|
432
|
+
* @example
|
|
433
|
+
* ```ts
|
|
434
|
+
* isToDay(new Date()) // true
|
|
435
|
+
* isToDay(new Date('2000-01-01')) // false
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
160
438
|
const isToDay = (date) => isSameDay(date, /* @__PURE__ */ new Date());
|
|
439
|
+
/**
|
|
440
|
+
* 判断两个日期是否为同一天
|
|
441
|
+
* @param dateA - 第一个日期
|
|
442
|
+
* @param dateB - 第二个日期
|
|
443
|
+
* @returns 是否为同一天
|
|
444
|
+
* @example
|
|
445
|
+
* ```ts
|
|
446
|
+
* const date1 = new Date('2024-01-15T10:30:00')
|
|
447
|
+
* const date2 = new Date('2024-01-15T23:59:59')
|
|
448
|
+
* isSameDay(date1, date2) // true
|
|
449
|
+
* ```
|
|
450
|
+
* @example
|
|
451
|
+
* ```ts
|
|
452
|
+
* const date1 = new Date('2024-01-15')
|
|
453
|
+
* const date2 = new Date('2024-01-16')
|
|
454
|
+
* isSameDay(date1, date2) // false
|
|
455
|
+
* ```
|
|
456
|
+
*/
|
|
161
457
|
function isSameDay(dateA, dateB) {
|
|
162
458
|
return dateA.getDate() === dateB.getDate() && dateA.getMonth() === dateB.getMonth() && dateA.getFullYear() === dateB.getFullYear();
|
|
163
459
|
}
|
|
164
460
|
|
|
165
461
|
//#endregion
|
|
166
462
|
//#region src/base/function.ts
|
|
463
|
+
/**
|
|
464
|
+
* 判断值是否为函数
|
|
465
|
+
* @param val - 要判断的值
|
|
466
|
+
* @returns 是否为函数
|
|
467
|
+
* @example
|
|
468
|
+
* ```ts
|
|
469
|
+
* isFunction(() => {}) // true
|
|
470
|
+
* isFunction(function() {}) // true
|
|
471
|
+
* isFunction('function') // false
|
|
472
|
+
* isFunction(null) // false
|
|
473
|
+
* ```
|
|
474
|
+
*/
|
|
167
475
|
const isFunction = (val) => typeof val === "function";
|
|
476
|
+
/**
|
|
477
|
+
* 空函数,什么都不做
|
|
478
|
+
* @returns void
|
|
479
|
+
* @example
|
|
480
|
+
* ```ts
|
|
481
|
+
* const callback: Fn = noop // 作为默认回调
|
|
482
|
+
* callback() // 什么都不执行
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
168
485
|
const noop = () => {};
|
|
486
|
+
/**
|
|
487
|
+
* 对值应用转换函数
|
|
488
|
+
* @param val - 输入值
|
|
489
|
+
* @param fn - 转换函数
|
|
490
|
+
* @returns 转换后的值
|
|
491
|
+
* @example
|
|
492
|
+
* ```ts
|
|
493
|
+
* transform(5, (x) => x * 2) // 10
|
|
494
|
+
* transform('hello', (s) => s.toUpperCase()) // 'HELLO'
|
|
495
|
+
* transform([1, 2, 3], (arr) => arr.length) // 3
|
|
496
|
+
* ```
|
|
497
|
+
*/
|
|
169
498
|
const transform = (val, fn) => fn(val);
|
|
170
499
|
|
|
171
500
|
//#endregion
|
|
172
501
|
//#region src/base/nullable.ts
|
|
502
|
+
/**
|
|
503
|
+
* 判断值是否为真值
|
|
504
|
+
* @param v - 要判断的值
|
|
505
|
+
* @returns 是否为真值
|
|
506
|
+
* @example
|
|
507
|
+
* ```ts
|
|
508
|
+
* isTruthy(true) // true
|
|
509
|
+
* isTruthy(1) // true
|
|
510
|
+
* isTruthy('hello') // true
|
|
511
|
+
* isTruthy({}) // true
|
|
512
|
+
* isTruthy([]) // true
|
|
513
|
+
* isTruthy(false) // false
|
|
514
|
+
* isTruthy(0) // false
|
|
515
|
+
* isTruthy('') // false
|
|
516
|
+
* isTruthy(null) // false
|
|
517
|
+
* isTruthy(undefined) // false
|
|
518
|
+
* ```
|
|
519
|
+
*/
|
|
173
520
|
const isTruthy = (v) => Boolean(v);
|
|
521
|
+
/**
|
|
522
|
+
* 判断值是否为 undefined
|
|
523
|
+
* @param val - 要判断的值
|
|
524
|
+
* @returns 是否为 undefined
|
|
525
|
+
* @example
|
|
526
|
+
* ```ts
|
|
527
|
+
* isUndefined(undefined) // true
|
|
528
|
+
* isUndefined(void 0) // true
|
|
529
|
+
* isUndefined(null) // false
|
|
530
|
+
* isUndefined('') // false
|
|
531
|
+
* ```
|
|
532
|
+
*/
|
|
174
533
|
const isUndefined = (val) => toString(val) === "[object Undefined]";
|
|
534
|
+
/**
|
|
535
|
+
* 判断值是否不为 undefined
|
|
536
|
+
* @param v - 要判断的值
|
|
537
|
+
* @returns 是否不为 undefined
|
|
538
|
+
* @example
|
|
539
|
+
* ```ts
|
|
540
|
+
* notUndefined('hello') // true
|
|
541
|
+
* notUndefined(0) // true
|
|
542
|
+
* notUndefined(null) // true
|
|
543
|
+
* notUndefined(undefined) // false
|
|
544
|
+
* ```
|
|
545
|
+
*/
|
|
175
546
|
const notUndefined = (v) => v !== void 0;
|
|
547
|
+
/**
|
|
548
|
+
* 判断值是否为 null
|
|
549
|
+
* @param val - 要判断的值
|
|
550
|
+
* @returns 是否为 null
|
|
551
|
+
* @example
|
|
552
|
+
* ```ts
|
|
553
|
+
* isNull(null) // true
|
|
554
|
+
* isNull(undefined) // false
|
|
555
|
+
* isNull('') // false
|
|
556
|
+
* isNull(0) // false
|
|
557
|
+
* ```
|
|
558
|
+
*/
|
|
176
559
|
const isNull = (val) => toString(val) === "[object Null]";
|
|
560
|
+
/**
|
|
561
|
+
* 判断值是否不为 null
|
|
562
|
+
* @param v - 要判断的值
|
|
563
|
+
* @returns 是否不为 null
|
|
564
|
+
* @example
|
|
565
|
+
* ```ts
|
|
566
|
+
* notNull('hello') // true
|
|
567
|
+
* notNull(0) // true
|
|
568
|
+
* notNull(undefined) // true
|
|
569
|
+
* notNull(null) // false
|
|
570
|
+
* ```
|
|
571
|
+
*/
|
|
177
572
|
const notNull = (v) => v !== null;
|
|
573
|
+
/**
|
|
574
|
+
* 判断值是否不为 null 且不为 undefined
|
|
575
|
+
* @param v - 要判断的值
|
|
576
|
+
* @returns 是否为非空值
|
|
577
|
+
* @example
|
|
578
|
+
* ```ts
|
|
579
|
+
* notNullish('hello') // true
|
|
580
|
+
* notNullish(0) // true
|
|
581
|
+
* notNullish(false) // true
|
|
582
|
+
* notNullish(null) // false
|
|
583
|
+
* notNullish(undefined) // false
|
|
584
|
+
* ```
|
|
585
|
+
*/
|
|
178
586
|
const notNullish = (v) => v != null;
|
|
587
|
+
/**
|
|
588
|
+
* 判断值是否已定义(不是 undefined)
|
|
589
|
+
* @param val - 要判断的值
|
|
590
|
+
* @returns 是否已定义
|
|
591
|
+
* @example
|
|
592
|
+
* ```ts
|
|
593
|
+
* isDefined('hello') // true
|
|
594
|
+
* isDefined(0) // true
|
|
595
|
+
* isDefined(null) // true
|
|
596
|
+
* isDefined(false) // true
|
|
597
|
+
* isDefined(undefined) // false
|
|
598
|
+
* ```
|
|
599
|
+
*/
|
|
179
600
|
const isDefined = (val) => typeof val !== "undefined";
|
|
180
601
|
|
|
181
602
|
//#endregion
|
|
182
603
|
//#region src/base/number.ts
|
|
604
|
+
/**
|
|
605
|
+
* 判断值是否为数字类型
|
|
606
|
+
* @param val - 要判断的值
|
|
607
|
+
* @returns 是否为数字
|
|
608
|
+
* @example
|
|
609
|
+
* ```ts
|
|
610
|
+
* isNumber(0) // true
|
|
611
|
+
* isNumber(1) // true
|
|
612
|
+
* isNumber(1.5) // true
|
|
613
|
+
* isNumber(Number.NaN) // true
|
|
614
|
+
* isNumber(Number.POSITIVE_INFINITY) // true
|
|
615
|
+
* isNumber('123') // false
|
|
616
|
+
* isNumber(null) // false
|
|
617
|
+
* ```
|
|
618
|
+
*/
|
|
183
619
|
const isNumber = (val) => typeof val === "number";
|
|
620
|
+
/**
|
|
621
|
+
* 将值限制在指定范围内
|
|
622
|
+
* @param n - 要限制的值
|
|
623
|
+
* @param min - 最小值
|
|
624
|
+
* @param max - 最大值
|
|
625
|
+
* @returns 限制后的值
|
|
626
|
+
* @example
|
|
627
|
+
* ```ts
|
|
628
|
+
* clamp(5, 0, 10) // 5 (在范围内)
|
|
629
|
+
* clamp(-5, 0, 10) // 0 (小于最小值,返回最小值)
|
|
630
|
+
* clamp(15, 0, 10) // 10 (大于最大值,返回最大值)
|
|
631
|
+
* clamp(1.5, 0, 1) // 1
|
|
632
|
+
* clamp(0.5, 0, 1) // 0.5
|
|
633
|
+
* ```
|
|
634
|
+
*/
|
|
184
635
|
const clamp = (n, min, max) => Math.min(max, Math.max(min, n));
|
|
636
|
+
/**
|
|
637
|
+
* 线性插值,返回两个值之间的插值
|
|
638
|
+
* @param min - 起始值
|
|
639
|
+
* @param max - 结束值
|
|
640
|
+
* @param t - 插值参数(0-1 之间)
|
|
641
|
+
* @returns 插值结果
|
|
642
|
+
* @example
|
|
643
|
+
* ```ts
|
|
644
|
+
* lerp(0, 100, 0.5) // 50 (中点)
|
|
645
|
+
* lerp(0, 100, 0) // 0 (起始点)
|
|
646
|
+
* lerp(0, 100, 1) // 100 (结束点)
|
|
647
|
+
* lerp(0, 100, -0.5) // 0 (t 被限制到 0)
|
|
648
|
+
* lerp(0, 100, 1.5) // 100 (t 被限制到 1)
|
|
649
|
+
* lerp(-100, 0, 0.5) // -50
|
|
650
|
+
* lerp(0, 10, 0.25) // 2.5
|
|
651
|
+
* ```
|
|
652
|
+
*/
|
|
185
653
|
function lerp(min, max, t) {
|
|
186
654
|
const interpolation = clamp(t, 0, 1);
|
|
187
655
|
return min + (max - min) * interpolation;
|
|
@@ -189,23 +657,112 @@ function lerp(min, max, t) {
|
|
|
189
657
|
|
|
190
658
|
//#endregion
|
|
191
659
|
//#region src/base/object.ts
|
|
660
|
+
/**
|
|
661
|
+
* 判断值是否为普通对象
|
|
662
|
+
* @param val - 要判断的值
|
|
663
|
+
* @returns 是否为对象
|
|
664
|
+
* @example
|
|
665
|
+
* ```ts
|
|
666
|
+
* isObject({}) // true
|
|
667
|
+
* isObject({ a: 1 }) // true
|
|
668
|
+
* isObject([]) // false
|
|
669
|
+
* isObject(null) // false
|
|
670
|
+
* isObject(new Date()) // false
|
|
671
|
+
* ```
|
|
672
|
+
*/
|
|
192
673
|
const isObject = (val) => toString(val) === "[object Object]";
|
|
674
|
+
/**
|
|
675
|
+
* 判断键是否为对象的键
|
|
676
|
+
* @param val - 对象
|
|
677
|
+
* @param key - 键
|
|
678
|
+
* @returns 是否为对象的键
|
|
679
|
+
* @example
|
|
680
|
+
* ```ts
|
|
681
|
+
* const obj = { a: 1, b: 2, c: 3 }
|
|
682
|
+
* isKeyOf(obj, 'a') // true
|
|
683
|
+
* isKeyOf(obj, 'b') // true
|
|
684
|
+
* isKeyOf(obj, 'd') // false
|
|
685
|
+
* // 注意:isKeyOf 使用 'key in val',所以继承属性也会返回 true
|
|
686
|
+
* isKeyOf(obj, 'toString') // true
|
|
687
|
+
* ```
|
|
688
|
+
*/
|
|
193
689
|
const isKeyOf = (val, key) => key in val;
|
|
690
|
+
/**
|
|
691
|
+
* 根据条件过滤对象的属性
|
|
692
|
+
* @param obj - 输入对象
|
|
693
|
+
* @param fn - 过滤函数
|
|
694
|
+
* @returns 过滤后的对象
|
|
695
|
+
* @example
|
|
696
|
+
* ```ts
|
|
697
|
+
* const obj = { a: 1, b: 2, c: 3, d: 4 }
|
|
698
|
+
* objectFilter(obj, (key, value) => value > 2) // { c: 3, d: 4 }
|
|
699
|
+
* objectFilter(obj, (key, value) => key === 'a') // { a: 1 }
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
194
702
|
function objectFilter(obj, fn) {
|
|
195
703
|
return Object.fromEntries(Object.entries(obj).filter(([key, value]) => fn(key, value)));
|
|
196
704
|
}
|
|
705
|
+
/**
|
|
706
|
+
* 从对象中选取指定的属性
|
|
707
|
+
* @param obj - 输入对象
|
|
708
|
+
* @param keys - 要选取的键
|
|
709
|
+
* @returns 包含指定属性的新对象
|
|
710
|
+
* @example
|
|
711
|
+
* ```ts
|
|
712
|
+
* const obj = { a: 1, b: 2, c: 3, d: 4 }
|
|
713
|
+
* objectPick(obj, 'a', 'c') // { a: 1, c: 3 }
|
|
714
|
+
* objectPick(obj, 'a', 'x') // { a: 1 }
|
|
715
|
+
* objectPick(obj) // {}
|
|
716
|
+
* ```
|
|
717
|
+
*/
|
|
197
718
|
function objectPick(obj, ...keys) {
|
|
198
719
|
return objectFilter(obj, (key) => keys.includes(key));
|
|
199
720
|
}
|
|
721
|
+
/**
|
|
722
|
+
* 从对象中排除指定的属性
|
|
723
|
+
* @param obj - 输入对象
|
|
724
|
+
* @param keys - 要排除的键
|
|
725
|
+
* @returns 不包含指定属性的新对象
|
|
726
|
+
* @example
|
|
727
|
+
* ```ts
|
|
728
|
+
* const obj = { a: 1, b: 2, c: 3, d: 4 }
|
|
729
|
+
* objectOmit(obj, 'a', 'c') // { b: 2, d: 4 }
|
|
730
|
+
* objectOmit(obj, 'a', 'x') // { b: 2, c: 3, d: 4 }
|
|
731
|
+
* objectOmit(obj) // { a: 1, b: 2, c: 3, d: 4 }
|
|
732
|
+
* ```
|
|
733
|
+
*/
|
|
200
734
|
function objectOmit(obj, ...keys) {
|
|
201
735
|
return objectFilter(obj, (key) => !keys.includes(key));
|
|
202
736
|
}
|
|
737
|
+
/**
|
|
738
|
+
* 清除对象中值为 undefined 的属性
|
|
739
|
+
* @param obj - 输入对象
|
|
740
|
+
* @returns 清除 undefined 后的对象
|
|
741
|
+
* @example
|
|
742
|
+
* ```ts
|
|
743
|
+
* clearUndefined({ a: 1, b: undefined, c: 3 }) // { a: 1, c: 3 }
|
|
744
|
+
* clearUndefined({ a: undefined }) // {}
|
|
745
|
+
* clearUndefined({ a: null, b: undefined }) // { a: null } // null 不会被清除
|
|
746
|
+
* ```
|
|
747
|
+
*/
|
|
203
748
|
function clearUndefined(obj) {
|
|
204
749
|
return objectFilter(obj, (_, value) => notUndefined(value));
|
|
205
750
|
}
|
|
206
751
|
|
|
207
752
|
//#endregion
|
|
208
753
|
//#region src/base/regexp.ts
|
|
754
|
+
/**
|
|
755
|
+
* 判断值是否为正则表达式
|
|
756
|
+
* @param val - 要判断的值
|
|
757
|
+
* @returns 是否为正则表达式
|
|
758
|
+
* @example
|
|
759
|
+
* ```ts
|
|
760
|
+
* isRegExp(/test/) // true
|
|
761
|
+
* isRegExp(new RegExp('test')) // true
|
|
762
|
+
* isRegExp('test') // false
|
|
763
|
+
* isRegExp(null) // false
|
|
764
|
+
* ```
|
|
765
|
+
*/
|
|
209
766
|
const isRegExp = (val) => toString(val) === "[object RegExp]";
|
|
210
767
|
|
|
211
768
|
//#endregion
|