ts-data-forge 2.1.1 → 2.1.3
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 +13 -15
- package/dist/array/array-utils.d.mts.map +1 -1
- package/dist/array/array-utils.mjs +9 -13
- package/dist/array/array-utils.mjs.map +1 -1
- package/dist/collections/imap.d.mts +1 -1
- package/dist/collections/imap.d.mts.map +1 -1
- package/dist/collections/imap.mjs +5 -5
- package/dist/collections/imap.mjs.map +1 -1
- package/dist/collections/iset-mapped.d.mts +1 -1
- package/dist/collections/iset-mapped.mjs +1 -1
- package/dist/collections/iset.d.mts +2 -2
- package/dist/collections/iset.mjs +4 -5
- package/dist/collections/iset.mjs.map +1 -1
- package/dist/collections/queue.d.mts +1 -1
- package/dist/collections/queue.d.mts.map +1 -1
- package/dist/collections/queue.mjs +7 -8
- package/dist/collections/queue.mjs.map +1 -1
- package/dist/collections/stack.d.mts +1 -1
- package/dist/collections/stack.d.mts.map +1 -1
- package/dist/collections/stack.mjs +6 -7
- package/dist/collections/stack.mjs.map +1 -1
- package/dist/entry-point.d.mts.map +1 -1
- package/dist/functional/match.d.mts.map +1 -1
- package/dist/functional/match.mjs +1 -6
- package/dist/functional/match.mjs.map +1 -1
- package/dist/functional/optional.d.mts.map +1 -1
- package/dist/functional/optional.mjs +6 -4
- package/dist/functional/optional.mjs.map +1 -1
- package/dist/functional/result.d.mts.map +1 -1
- package/dist/functional/result.mjs +1 -6
- package/dist/functional/result.mjs.map +1 -1
- package/dist/json/json.d.mts.map +1 -1
- package/dist/json/json.mjs +3 -3
- package/dist/json/json.mjs.map +1 -1
- package/dist/others/memoize-function.d.mts +1 -1
- package/dist/others/memoize-function.mjs +1 -1
- package/dist/others/unknown-to-string.d.mts +44 -77
- package/dist/others/unknown-to-string.d.mts.map +1 -1
- package/dist/others/unknown-to-string.mjs +51 -85
- package/dist/others/unknown-to-string.mjs.map +1 -1
- package/package.json +10 -1
- package/src/array/array-utils-creation.test.mts +9 -8
- package/src/array/array-utils-overload-type-error.test.mts +6 -2
- package/src/array/array-utils-search.test.mts +1 -1
- package/src/array/array-utils-slicing.test.mts +0 -42
- package/src/array/array-utils-transformation.test.mts +11 -186
- package/src/array/array-utils-validation.test.mts +27 -27
- package/src/array/array-utils.mts +8 -12
- package/src/collections/imap-mapped.test.mts +15 -11
- package/src/collections/imap.mts +6 -13
- package/src/collections/imap.test.mts +20 -19
- package/src/collections/iset-mapped.mts +1 -1
- package/src/collections/iset-mapped.test.mts +14 -104
- package/src/collections/iset.mts +5 -7
- package/src/collections/iset.test.mts +43 -34
- package/src/collections/queue.mts +12 -10
- package/src/collections/queue.test.mts +46 -44
- package/src/collections/stack.mts +10 -9
- package/src/collections/stack.test.mts +12 -10
- package/src/entry-point.mts +1 -0
- package/src/functional/match.mts +1 -5
- package/src/functional/optional.mts +6 -6
- package/src/functional/optional.test.mts +7 -7
- package/src/functional/result.mts +1 -5
- package/src/guard/is-non-empty-string.test.mts +1 -1
- package/src/guard/is-non-null-object.test.mts +3 -3
- package/src/guard/is-primitive.test.mts +6 -6
- package/src/guard/is-type.test.mts +8 -12
- package/src/iterator/range.test.mts +1 -1
- package/src/json/json.mts +4 -11
- package/src/json/json.test.mts +25 -28
- package/src/number/branded-types/finite-number.test.mts +2 -1
- package/src/number/branded-types/int.test.mts +2 -1
- package/src/number/branded-types/int16.test.mts +3 -2
- package/src/number/branded-types/int32.test.mts +3 -2
- package/src/number/branded-types/non-negative-finite-number.test.mts +3 -2
- package/src/number/branded-types/non-negative-int16.test.mts +3 -2
- package/src/number/branded-types/non-negative-int32.test.mts +3 -2
- package/src/number/branded-types/non-zero-finite-number.test.mts +4 -3
- package/src/number/branded-types/non-zero-int.test.mts +4 -3
- package/src/number/branded-types/non-zero-int16.test.mts +3 -2
- package/src/number/branded-types/non-zero-int32.test.mts +3 -2
- package/src/number/branded-types/non-zero-safe-int.test.mts +4 -3
- package/src/number/branded-types/non-zero-uint16.test.mts +3 -2
- package/src/number/branded-types/non-zero-uint32.test.mts +3 -2
- package/src/number/branded-types/positive-finite-number.test.mts +3 -2
- package/src/number/branded-types/positive-int.test.mts +3 -2
- package/src/number/branded-types/positive-int16.test.mts +3 -2
- package/src/number/branded-types/positive-int32.test.mts +3 -2
- package/src/number/branded-types/positive-safe-int.test.mts +3 -2
- package/src/number/branded-types/positive-uint16.test.mts +3 -2
- package/src/number/branded-types/positive-uint32.test.mts +3 -2
- package/src/number/branded-types/safe-int.test.mts +3 -2
- package/src/number/branded-types/safe-uint.test.mts +3 -2
- package/src/number/branded-types/uint.test.mts +3 -2
- package/src/number/branded-types/uint16.test.mts +3 -2
- package/src/number/branded-types/uint32.test.mts +3 -2
- package/src/object/object.test.mts +10 -10
- package/src/others/cast-readonly.test.mts +8 -8
- package/src/others/memoize-function.mts +1 -1
- package/src/others/memoize-function.test.mts +2 -2
- package/src/others/unknown-to-string.mts +52 -87
- package/src/others/unknown-to-string.test.mts +26 -58
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IMap } from '../collections/index.mjs';
|
|
2
2
|
import { expectType } from '../expect-type.mjs';
|
|
3
|
-
import { Optional } from '../functional/
|
|
3
|
+
import { Optional } from '../functional/index.mjs';
|
|
4
4
|
import { SafeUint } from '../number/index.mjs';
|
|
5
5
|
import { Arr } from './array-utils.mjs';
|
|
6
6
|
|
|
@@ -132,47 +132,7 @@ describe('Arr transformations', () => {
|
|
|
132
132
|
expect(result).toStrictEqual([[1, 2]]);
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
-
test('should work with empty array', () => {
|
|
136
|
-
const empty: readonly number[] = [];
|
|
137
|
-
const result = Arr.partition(empty, 2);
|
|
138
|
-
|
|
139
|
-
expect(result).toStrictEqual([]);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test('should partition array into chunks', () => {
|
|
143
|
-
const numbers = [1, 2, 3, 4, 5, 6];
|
|
144
|
-
const result = Arr.partition(numbers, 2);
|
|
145
|
-
|
|
146
|
-
expect(result).toStrictEqual([
|
|
147
|
-
[1, 2],
|
|
148
|
-
[3, 4],
|
|
149
|
-
[5, 6],
|
|
150
|
-
]);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test('should handle arrays not evenly divisible by chunk size', () => {
|
|
154
|
-
const numbers = [1, 2, 3, 4, 5];
|
|
155
|
-
const result = Arr.partition(numbers, 2);
|
|
156
|
-
|
|
157
|
-
expect(result).toStrictEqual([[1, 2], [3, 4], [5]]);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
test('should work with chunk size < 2 (returns empty)', () => {
|
|
161
|
-
const numbers = [1, 2, 3];
|
|
162
|
-
const result = Arr.partition(numbers, 1);
|
|
163
|
-
|
|
164
|
-
// According to docs, returns empty array if chunkSize < 2
|
|
165
|
-
expect(result).toStrictEqual([]);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test('should work with chunk size larger than array', () => {
|
|
169
|
-
const numbers = [1, 2];
|
|
170
|
-
const result = Arr.partition(numbers, 5);
|
|
171
|
-
|
|
172
|
-
expect(result).toStrictEqual([[1, 2]]);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test('should work with empty array', () => {
|
|
135
|
+
test('partition should work with empty array', () => {
|
|
176
136
|
const empty: readonly number[] = [];
|
|
177
137
|
const result = Arr.partition(empty, 2);
|
|
178
138
|
|
|
@@ -180,19 +140,6 @@ describe('Arr transformations', () => {
|
|
|
180
140
|
});
|
|
181
141
|
});
|
|
182
142
|
|
|
183
|
-
describe('toReversed', () => {
|
|
184
|
-
{
|
|
185
|
-
const xs = [1, 2, 3] as const;
|
|
186
|
-
const result = xs.toReversed();
|
|
187
|
-
|
|
188
|
-
expectType<typeof result, (1 | 2 | 3)[]>('=');
|
|
189
|
-
|
|
190
|
-
test('case 1', () => {
|
|
191
|
-
expect(result).toStrictEqual([3, 2, 1]);
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
|
|
196
143
|
describe('toSorted', () => {
|
|
197
144
|
{
|
|
198
145
|
const xs = [2, 1, 3] as const;
|
|
@@ -294,37 +241,7 @@ describe('Arr transformations', () => {
|
|
|
294
241
|
expect(result).toStrictEqual([]);
|
|
295
242
|
});
|
|
296
243
|
|
|
297
|
-
test('should
|
|
298
|
-
const people = [
|
|
299
|
-
{ name: 'Alice', age: 30 },
|
|
300
|
-
{ name: 'Bob', age: 20 },
|
|
301
|
-
{ name: 'Charlie', age: 25 },
|
|
302
|
-
];
|
|
303
|
-
const result = Arr.toSortedBy(people, (person) => person.age);
|
|
304
|
-
|
|
305
|
-
expect(result).toHaveLength(3);
|
|
306
|
-
expect(result[0]?.name).toBe('Bob');
|
|
307
|
-
expect(result[1]?.name).toBe('Charlie');
|
|
308
|
-
expect(result[2]?.name).toBe('Alice');
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
test('should work with string sorting', () => {
|
|
312
|
-
const words = ['banana', 'apple', 'cherry'];
|
|
313
|
-
const result = Arr.toSortedBy(
|
|
314
|
-
words,
|
|
315
|
-
(word: string) => word,
|
|
316
|
-
(a: string, b: string) => a.localeCompare(b),
|
|
317
|
-
);
|
|
318
|
-
expect(result).toStrictEqual(['apple', 'banana', 'cherry']);
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
test('should work with custom key extraction', () => {
|
|
322
|
-
const items = ['hello', 'hi', 'welcome', 'bye'];
|
|
323
|
-
const result = Arr.toSortedBy(items, (item) => item.length);
|
|
324
|
-
expect(result).toStrictEqual(['hi', 'bye', 'hello', 'welcome']);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
test('should work with empty array', () => {
|
|
244
|
+
test('toSortedBy should work with empty array', () => {
|
|
328
245
|
const empty: readonly { value: number }[] = [];
|
|
329
246
|
const result = Arr.toSortedBy(empty, (item) => item.value);
|
|
330
247
|
expect(result).toStrictEqual([]);
|
|
@@ -452,68 +369,6 @@ describe('Arr transformations', () => {
|
|
|
452
369
|
expect(all.value).toStrictEqual([1, 2, 3, 4]);
|
|
453
370
|
}
|
|
454
371
|
});
|
|
455
|
-
|
|
456
|
-
test('should group elements by key', () => {
|
|
457
|
-
const array = [
|
|
458
|
-
{ type: 'fruit', name: 'apple' },
|
|
459
|
-
{ type: 'vegetable', name: 'carrot' },
|
|
460
|
-
{ type: 'fruit', name: 'banana' },
|
|
461
|
-
];
|
|
462
|
-
const grouped = Arr.groupBy(array, (item) => item.type);
|
|
463
|
-
|
|
464
|
-
expect(grouped.size).toBe(2);
|
|
465
|
-
const fruits = grouped.get('fruit');
|
|
466
|
-
const vegetables = grouped.get('vegetable');
|
|
467
|
-
|
|
468
|
-
expect(Optional.isSome(fruits)).toBe(true);
|
|
469
|
-
expect(Optional.isSome(vegetables)).toBe(true);
|
|
470
|
-
|
|
471
|
-
if (Optional.isSome(fruits)) {
|
|
472
|
-
expect(fruits.value).toHaveLength(2);
|
|
473
|
-
expect(fruits.value[0]?.name).toBe('apple');
|
|
474
|
-
expect(fruits.value[1]?.name).toBe('banana');
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
if (Optional.isSome(vegetables)) {
|
|
478
|
-
expect(vegetables.value).toHaveLength(1);
|
|
479
|
-
expect(vegetables.value[0]?.name).toBe('carrot');
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
test('should work with numeric keys', () => {
|
|
484
|
-
const numbers = [1, 2, 3, 4, 5, 6];
|
|
485
|
-
const grouped = Arr.groupBy(numbers, (n) => n % 2);
|
|
486
|
-
|
|
487
|
-
expect(grouped.size).toBe(2);
|
|
488
|
-
const evens = grouped.get(0);
|
|
489
|
-
const odds = grouped.get(1);
|
|
490
|
-
|
|
491
|
-
if (Optional.isSome(evens)) {
|
|
492
|
-
expect(evens.value).toStrictEqual([2, 4, 6]);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
if (Optional.isSome(odds)) {
|
|
496
|
-
expect(odds.value).toStrictEqual([1, 3, 5]);
|
|
497
|
-
}
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
test('should work with empty array', () => {
|
|
501
|
-
const empty: readonly number[] = [];
|
|
502
|
-
const grouped = Arr.groupBy(empty, (n) => n % 2);
|
|
503
|
-
expect(grouped.size).toBe(0);
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
test('should handle all elements in same group', () => {
|
|
507
|
-
const array = [1, 2, 3, 4];
|
|
508
|
-
const grouped = Arr.groupBy(array, () => 'all');
|
|
509
|
-
|
|
510
|
-
expect(grouped.size).toBe(1);
|
|
511
|
-
const all = grouped.get('all');
|
|
512
|
-
|
|
513
|
-
if (Optional.isSome(all)) {
|
|
514
|
-
expect(all.value).toStrictEqual([1, 2, 3, 4]);
|
|
515
|
-
}
|
|
516
|
-
});
|
|
517
372
|
});
|
|
518
373
|
|
|
519
374
|
describe('zip', () => {
|
|
@@ -632,41 +487,6 @@ describe('Arr transformations', () => {
|
|
|
632
487
|
const result = Arr.zip(arr1, arr2);
|
|
633
488
|
expect(result).toStrictEqual([]);
|
|
634
489
|
});
|
|
635
|
-
|
|
636
|
-
test('should zip two arrays', () => {
|
|
637
|
-
const arr1 = [1, 2, 3];
|
|
638
|
-
const arr2 = ['a', 'b', 'c'];
|
|
639
|
-
const result = Arr.zip(arr1, arr2);
|
|
640
|
-
expect(result).toStrictEqual([
|
|
641
|
-
[1, 'a'],
|
|
642
|
-
[2, 'b'],
|
|
643
|
-
[3, 'c'],
|
|
644
|
-
]);
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
test('should handle arrays of different lengths', () => {
|
|
648
|
-
const arr1 = [1, 2, 3, 4];
|
|
649
|
-
const arr2 = ['a', 'b'];
|
|
650
|
-
const result = Arr.zip(arr1, arr2);
|
|
651
|
-
expect(result).toStrictEqual([
|
|
652
|
-
[1, 'a'],
|
|
653
|
-
[2, 'b'],
|
|
654
|
-
]);
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
test('should work with empty arrays', () => {
|
|
658
|
-
const arr1: readonly number[] = [];
|
|
659
|
-
const arr2: readonly string[] = [];
|
|
660
|
-
const result = Arr.zip(arr1, arr2);
|
|
661
|
-
expect(result).toStrictEqual([]);
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
test('should handle one empty array', () => {
|
|
665
|
-
const arr1 = [1, 2, 3];
|
|
666
|
-
const arr2: readonly string[] = [];
|
|
667
|
-
const result = Arr.zip(arr1, arr2);
|
|
668
|
-
expect(result).toStrictEqual([]);
|
|
669
|
-
});
|
|
670
490
|
});
|
|
671
491
|
|
|
672
492
|
describe('filterNot', () => {
|
|
@@ -816,6 +636,11 @@ describe('Arr transformations', () => {
|
|
|
816
636
|
|
|
817
637
|
describe('toReversed', () => {
|
|
818
638
|
{
|
|
639
|
+
const xs = [1, 2, 3] as const;
|
|
640
|
+
const _nativeResult = xs.toReversed();
|
|
641
|
+
|
|
642
|
+
expectType<typeof _nativeResult, (1 | 2 | 3)[]>('=');
|
|
643
|
+
|
|
819
644
|
const result = Arr.toReversed([1, 2, 3]);
|
|
820
645
|
|
|
821
646
|
expectType<typeof result, readonly [3, 2, 1]>('=');
|
|
@@ -1197,15 +1022,15 @@ describe('Arr transformations', () => {
|
|
|
1197
1022
|
test('should search from end to beginning', () => {
|
|
1198
1023
|
// Verify search order by using side effects
|
|
1199
1024
|
const numbers = [1, 2, 3, 4, 5];
|
|
1200
|
-
const
|
|
1025
|
+
const mut_searchOrder: number[] = [];
|
|
1201
1026
|
|
|
1202
1027
|
Arr.findLastIndex(numbers, (val, idx) => {
|
|
1203
|
-
|
|
1028
|
+
mut_searchOrder.push(idx);
|
|
1204
1029
|
return val === 3;
|
|
1205
1030
|
});
|
|
1206
1031
|
|
|
1207
1032
|
// Should search from end: 4, 3, 2 (stops at 2 when found)
|
|
1208
|
-
expect(
|
|
1033
|
+
expect(mut_searchOrder).toStrictEqual([4, 3, 2]);
|
|
1209
1034
|
});
|
|
1210
1035
|
|
|
1211
1036
|
test('should handle single element array', () => {
|
|
@@ -19,14 +19,16 @@ describe('Arr validations', () => {
|
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
test('should refine union types correctly', () => {
|
|
22
|
-
|
|
22
|
+
const processValue = (
|
|
23
|
+
value: string | readonly number[] | null,
|
|
24
|
+
): number => {
|
|
23
25
|
if (Arr.isArray(value)) {
|
|
24
26
|
// value should be typed as number[]
|
|
25
27
|
expectType<typeof value, readonly number[]>('=');
|
|
26
28
|
return value.length;
|
|
27
29
|
}
|
|
28
30
|
return 0;
|
|
29
|
-
}
|
|
31
|
+
};
|
|
30
32
|
|
|
31
33
|
expect(processValue([1, 2, 3])).toBe(3);
|
|
32
34
|
expect(processValue('hello')).toBe(0);
|
|
@@ -37,7 +39,7 @@ describe('Arr validations', () => {
|
|
|
37
39
|
const readonlyArray: readonly number[] = [1, 2, 3];
|
|
38
40
|
if (Arr.isArray(readonlyArray)) {
|
|
39
41
|
expectType<typeof readonlyArray, readonly number[]>('=');
|
|
40
|
-
expect(readonlyArray
|
|
42
|
+
expect(readonlyArray).toHaveLength(3);
|
|
41
43
|
}
|
|
42
44
|
});
|
|
43
45
|
|
|
@@ -45,25 +47,25 @@ describe('Arr validations', () => {
|
|
|
45
47
|
const mutableArray: number[] = [1, 2, 3];
|
|
46
48
|
if (Arr.isArray(mutableArray)) {
|
|
47
49
|
expectType<typeof mutableArray, number[]>('=');
|
|
48
|
-
expect(mutableArray
|
|
50
|
+
expect(mutableArray).toHaveLength(3);
|
|
49
51
|
}
|
|
50
52
|
});
|
|
51
53
|
|
|
52
54
|
test('should exclude impossible array types from unions', () => {
|
|
53
|
-
|
|
54
|
-
value: string | boolean | readonly number[] | {
|
|
55
|
-
): number {
|
|
55
|
+
const checkUnion = (
|
|
56
|
+
value: string | boolean | readonly number[] | Readonly<{ a: number }>,
|
|
57
|
+
): number => {
|
|
56
58
|
if (Arr.isArray(value)) {
|
|
57
59
|
// Only number[] should remain
|
|
58
60
|
expectType<typeof value, readonly number[]>('=');
|
|
59
61
|
return value.length;
|
|
60
62
|
}
|
|
61
63
|
// Non-array types
|
|
62
|
-
expectType<typeof value, string | boolean | {
|
|
64
|
+
expectType<typeof value, string | boolean | Readonly<{ a: number }>>(
|
|
63
65
|
'=',
|
|
64
66
|
);
|
|
65
67
|
return -1;
|
|
66
|
-
}
|
|
68
|
+
};
|
|
67
69
|
|
|
68
70
|
expect(checkUnion([1, 2])).toBe(2);
|
|
69
71
|
expect(checkUnion('test')).toBe(-1);
|
|
@@ -72,17 +74,17 @@ describe('Arr validations', () => {
|
|
|
72
74
|
});
|
|
73
75
|
|
|
74
76
|
test('should exclude impossible array types from unions (including unknown)', () => {
|
|
75
|
-
|
|
77
|
+
const checkUnion = (
|
|
76
78
|
value:
|
|
77
79
|
| string
|
|
78
80
|
| boolean
|
|
79
81
|
| readonly number[]
|
|
80
|
-
| {
|
|
82
|
+
| Readonly<{ a: number }>
|
|
81
83
|
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
|
82
84
|
| unknown
|
|
83
85
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
|
84
86
|
| object,
|
|
85
|
-
): number {
|
|
87
|
+
): number => {
|
|
86
88
|
if (Arr.isArray(value)) {
|
|
87
89
|
// Only number[] should remain
|
|
88
90
|
expectType<typeof value, readonly unknown[]>('=');
|
|
@@ -93,14 +95,14 @@ describe('Arr validations', () => {
|
|
|
93
95
|
typeof value,
|
|
94
96
|
| string
|
|
95
97
|
| boolean
|
|
96
|
-
| {
|
|
98
|
+
| Readonly<{ a: number }>
|
|
97
99
|
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
|
98
100
|
| unknown
|
|
99
101
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
|
100
102
|
| object
|
|
101
103
|
>('=');
|
|
102
104
|
return -1;
|
|
103
|
-
}
|
|
105
|
+
};
|
|
104
106
|
|
|
105
107
|
expect(checkUnion([1, 2])).toBe(2);
|
|
106
108
|
expect(checkUnion('test')).toBe(-1);
|
|
@@ -126,7 +128,7 @@ describe('Arr validations', () => {
|
|
|
126
128
|
const value: unknown = [1, 2, 3];
|
|
127
129
|
if (Arr.isArray(value)) {
|
|
128
130
|
expectType<typeof value, readonly unknown[]>('=');
|
|
129
|
-
expect(value
|
|
131
|
+
expect(value).toHaveLength(3);
|
|
130
132
|
}
|
|
131
133
|
});
|
|
132
134
|
|
|
@@ -186,13 +188,13 @@ describe('Arr validations', () => {
|
|
|
186
188
|
});
|
|
187
189
|
|
|
188
190
|
test('should work with generic function', () => {
|
|
189
|
-
|
|
191
|
+
const processGeneric = <T,>(value: T | readonly number[]): number => {
|
|
190
192
|
if (Arr.isArray(value)) {
|
|
191
193
|
// Type is narrowed to array type within this block
|
|
192
194
|
return value.length;
|
|
193
195
|
}
|
|
194
196
|
return 0;
|
|
195
|
-
}
|
|
197
|
+
};
|
|
196
198
|
expect(processGeneric([1, 2, 3])).toBe(3);
|
|
197
199
|
expect(processGeneric('hello')).toBe(0);
|
|
198
200
|
});
|
|
@@ -207,14 +209,14 @@ describe('Arr validations', () => {
|
|
|
207
209
|
|
|
208
210
|
test('should work with conditional types', () => {
|
|
209
211
|
type ArrayOrValue<T> = T extends readonly unknown[] ? T : readonly T[];
|
|
210
|
-
|
|
212
|
+
const makeArray = <T,>(value: T): ArrayOrValue<T> => {
|
|
211
213
|
if (Arr.isArray(value)) {
|
|
212
214
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
213
215
|
return value as ArrayOrValue<T>;
|
|
214
216
|
}
|
|
215
217
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
216
218
|
return [value] as ArrayOrValue<T>;
|
|
217
|
-
}
|
|
219
|
+
};
|
|
218
220
|
expect(makeArray([1, 2, 3])).toStrictEqual([1, 2, 3]);
|
|
219
221
|
expect(makeArray(5)).toStrictEqual([5]);
|
|
220
222
|
});
|
|
@@ -229,9 +231,7 @@ describe('Arr validations', () => {
|
|
|
229
231
|
});
|
|
230
232
|
|
|
231
233
|
test('should work with branded types', () => {
|
|
232
|
-
type BrandedArray = readonly number[] & {
|
|
233
|
-
readonly __brand: unique symbol;
|
|
234
|
-
};
|
|
234
|
+
type BrandedArray = readonly number[] & Readonly<{ __brand: unknown }>;
|
|
235
235
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
236
236
|
const branded = [1, 2, 3] as unknown as BrandedArray;
|
|
237
237
|
if (Arr.isArray(branded)) {
|
|
@@ -248,7 +248,7 @@ describe('Arr validations', () => {
|
|
|
248
248
|
| null;
|
|
249
249
|
|
|
250
250
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
251
|
-
|
|
251
|
+
const processComplex = (value: ComplexUnion): number => {
|
|
252
252
|
if (Arr.isArray(value)) {
|
|
253
253
|
expectType<typeof value, readonly number[]>('=');
|
|
254
254
|
return value.length;
|
|
@@ -267,7 +267,7 @@ describe('Arr validations', () => {
|
|
|
267
267
|
| { type: 'object'; data: Record<string, unknown> }
|
|
268
268
|
>('=');
|
|
269
269
|
return -1;
|
|
270
|
-
}
|
|
270
|
+
};
|
|
271
271
|
|
|
272
272
|
expect(processComplex([1, 2, 3])).toBe(3);
|
|
273
273
|
expect(processComplex('test')).toBe(4);
|
|
@@ -293,7 +293,7 @@ describe('Arr validations', () => {
|
|
|
293
293
|
const sym = Symbol('test');
|
|
294
294
|
const arrWithSymbol = Object.assign([1, 2, 3], { [sym]: 'value' });
|
|
295
295
|
if (Arr.isArray(arrWithSymbol)) {
|
|
296
|
-
expect(arrWithSymbol
|
|
296
|
+
expect(arrWithSymbol).toHaveLength(3);
|
|
297
297
|
}
|
|
298
298
|
});
|
|
299
299
|
});
|
|
@@ -390,7 +390,7 @@ describe('Arr validations', () => {
|
|
|
390
390
|
const array: readonly number[] = [1, 2, 3];
|
|
391
391
|
if (Arr.isArrayOfLength(array, 3)) {
|
|
392
392
|
expectType<typeof array, ArrayOfLength<3, number>>('=');
|
|
393
|
-
expect(array
|
|
393
|
+
expect(array).toHaveLength(3);
|
|
394
394
|
}
|
|
395
395
|
});
|
|
396
396
|
});
|
|
@@ -455,7 +455,7 @@ describe('Arr validations', () => {
|
|
|
455
455
|
const array: readonly number[] = [1, 2, 3];
|
|
456
456
|
if (Arr.isArrayAtLeastLength(array, 2)) {
|
|
457
457
|
expectType<typeof array, ArrayAtLeastLen<2, number>>('=');
|
|
458
|
-
expect(array.length
|
|
458
|
+
expect(array.length).toBeGreaterThanOrEqual(2);
|
|
459
459
|
}
|
|
460
460
|
});
|
|
461
461
|
});
|
|
@@ -2133,9 +2133,9 @@ export namespace Arr {
|
|
|
2133
2133
|
switch (args.length) {
|
|
2134
2134
|
case 3: {
|
|
2135
2135
|
const [array, value, [start, end]] = args;
|
|
2136
|
-
const
|
|
2137
|
-
|
|
2138
|
-
return
|
|
2136
|
+
const mut_cp: (E | V)[] = castMutable(copy(array));
|
|
2137
|
+
mut_cp.fill(value, start, end);
|
|
2138
|
+
return mut_cp;
|
|
2139
2139
|
}
|
|
2140
2140
|
case 2: {
|
|
2141
2141
|
const [value, fillRange] = args;
|
|
@@ -2669,7 +2669,7 @@ export namespace Arr {
|
|
|
2669
2669
|
const [array, searchElement] = args;
|
|
2670
2670
|
|
|
2671
2671
|
const index = array.indexOf(searchElement);
|
|
2672
|
-
return index
|
|
2672
|
+
return index !== -1 ? asUint32(index) : -1;
|
|
2673
2673
|
}
|
|
2674
2674
|
case 1: {
|
|
2675
2675
|
const [searchElement] = args;
|
|
@@ -2702,7 +2702,7 @@ export namespace Arr {
|
|
|
2702
2702
|
case 3: {
|
|
2703
2703
|
const [array, searchElement, fromIndex] = args;
|
|
2704
2704
|
const index = array.indexOf(searchElement, fromIndex);
|
|
2705
|
-
return index
|
|
2705
|
+
return index !== -1 ? asUint32(index) : -1;
|
|
2706
2706
|
}
|
|
2707
2707
|
case 2: {
|
|
2708
2708
|
const [searchElement, fromIndex] = args;
|
|
@@ -2750,7 +2750,7 @@ export namespace Arr {
|
|
|
2750
2750
|
case 2: {
|
|
2751
2751
|
const [array, searchElement] = args;
|
|
2752
2752
|
const index = array.lastIndexOf(searchElement);
|
|
2753
|
-
return index
|
|
2753
|
+
return index !== -1 ? asUint32(index) : -1;
|
|
2754
2754
|
}
|
|
2755
2755
|
case 1: {
|
|
2756
2756
|
const [searchElement] = args;
|
|
@@ -2785,7 +2785,7 @@ export namespace Arr {
|
|
|
2785
2785
|
|
|
2786
2786
|
const index = array.lastIndexOf(searchElement, fromIndex);
|
|
2787
2787
|
|
|
2788
|
-
return index
|
|
2788
|
+
return index !== -1 ? asUint32(index) : -1;
|
|
2789
2789
|
}
|
|
2790
2790
|
case 2: {
|
|
2791
2791
|
const [searchElement, fromIndex] = args;
|
|
@@ -3460,11 +3460,7 @@ export namespace Arr {
|
|
|
3460
3460
|
return Result.ok(result);
|
|
3461
3461
|
} catch (error) {
|
|
3462
3462
|
return Result.err(
|
|
3463
|
-
error instanceof Error
|
|
3464
|
-
? error
|
|
3465
|
-
: pipe(unknownToString(error))
|
|
3466
|
-
.map(Result.unwrapOkOr('Failed to join array'))
|
|
3467
|
-
.map((e) => new Error(e)).value,
|
|
3463
|
+
error instanceof Error ? error : new Error(unknownToString(error)),
|
|
3468
3464
|
);
|
|
3469
3465
|
}
|
|
3470
3466
|
};
|
|
@@ -83,10 +83,12 @@ describe('IMapMapped.create', () => {
|
|
|
83
83
|
|
|
84
84
|
test('should handle complex key transformations', () => {
|
|
85
85
|
type ComplexKey = { nested: { id: number }; arr: number[] };
|
|
86
|
-
const complexKeyToString = (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
const complexKeyToString = (
|
|
87
|
+
key: DeepReadonly<{
|
|
88
|
+
nested: { id: number };
|
|
89
|
+
arr: number[];
|
|
90
|
+
}>,
|
|
91
|
+
): string => `${key.nested.id}_${key.arr.join(',')}`;
|
|
90
92
|
const stringToComplexKey = (str: string): ComplexKey => {
|
|
91
93
|
const [idStr, arrStr] = str.split('_');
|
|
92
94
|
return {
|
|
@@ -527,6 +529,7 @@ describe('IMapMapped.some', () => {
|
|
|
527
529
|
testKeyToString,
|
|
528
530
|
stringToTestKey,
|
|
529
531
|
);
|
|
532
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
530
533
|
expect(map.some((value) => value === 'Alice')).toBe(true);
|
|
531
534
|
});
|
|
532
535
|
|
|
@@ -539,6 +542,7 @@ describe('IMapMapped.some', () => {
|
|
|
539
542
|
testKeyToString,
|
|
540
543
|
stringToTestKey,
|
|
541
544
|
);
|
|
545
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
542
546
|
expect(map.some((value) => value === 'Charlie')).toBe(false);
|
|
543
547
|
});
|
|
544
548
|
|
|
@@ -704,15 +708,15 @@ describe('IMapMapped.forEach', () => {
|
|
|
704
708
|
testKeyToString,
|
|
705
709
|
stringToTestKey,
|
|
706
710
|
);
|
|
707
|
-
const
|
|
711
|
+
const mut_collected: [TestKey, string][] = [];
|
|
708
712
|
|
|
709
|
-
map.
|
|
710
|
-
|
|
711
|
-
}
|
|
713
|
+
for (const [key, value] of map.entries()) {
|
|
714
|
+
mut_collected.push([key, value]);
|
|
715
|
+
}
|
|
712
716
|
|
|
713
|
-
expect(
|
|
714
|
-
expect(
|
|
715
|
-
expect(
|
|
717
|
+
expect(mut_collected).toHaveLength(2);
|
|
718
|
+
expect(mut_collected).toContainEqual([{ id: 1, type: 'user' }, 'Alice']);
|
|
719
|
+
expect(mut_collected).toContainEqual([{ id: 2, type: 'admin' }, 'Bob']);
|
|
716
720
|
});
|
|
717
721
|
});
|
|
718
722
|
|
package/src/collections/imap.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { Optional
|
|
1
|
+
import { Optional } from '../functional/index.mjs';
|
|
2
2
|
import { asUint32 } from '../number/index.mjs';
|
|
3
|
-
import { tp } from '../others/index.mjs';
|
|
4
|
-
import { unknownToString } from '../others/unknown-to-string.mjs';
|
|
3
|
+
import { tp, unknownToString } from '../others/index.mjs';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Interface for an immutable map with O(1) lookup performance and functional programming patterns.
|
|
@@ -323,7 +322,7 @@ export namespace IMap {
|
|
|
323
322
|
*
|
|
324
323
|
* // From custom iterable
|
|
325
324
|
* function* generateEntries(): Generator<[string, number]> {
|
|
326
|
-
* for (
|
|
325
|
+
* for (const i of range(3)) {
|
|
327
326
|
* yield [`item${i}`, i * 10];
|
|
328
327
|
* }
|
|
329
328
|
* }
|
|
@@ -488,9 +487,7 @@ class IMapClass<K extends MapSetKeyType, V>
|
|
|
488
487
|
if (!this.has(key)) {
|
|
489
488
|
if (this.#showNotFoundMessage) {
|
|
490
489
|
const keyStr = unknownToString(key);
|
|
491
|
-
console.warn(
|
|
492
|
-
`IMap.delete: key not found: ${Result.isOk(keyStr) ? keyStr.value : '<error converting key to string>'}`,
|
|
493
|
-
);
|
|
490
|
+
console.warn(`IMap.delete: key not found: ${keyStr}`);
|
|
494
491
|
}
|
|
495
492
|
return this;
|
|
496
493
|
}
|
|
@@ -519,9 +516,7 @@ class IMapClass<K extends MapSetKeyType, V>
|
|
|
519
516
|
if (Optional.isNone(curr)) {
|
|
520
517
|
if (this.#showNotFoundMessage) {
|
|
521
518
|
const keyStr = unknownToString(key);
|
|
522
|
-
console.warn(
|
|
523
|
-
`IMap.update: key not found: ${Result.isOk(keyStr) ? keyStr.value : '<error converting key to string>'}`,
|
|
524
|
-
);
|
|
519
|
+
console.warn(`IMap.update: key not found: ${keyStr}`);
|
|
525
520
|
}
|
|
526
521
|
return this;
|
|
527
522
|
}
|
|
@@ -561,9 +556,7 @@ class IMapClass<K extends MapSetKeyType, V>
|
|
|
561
556
|
if (!mut_result.has(key) || curr === undefined) {
|
|
562
557
|
if (this.#showNotFoundMessage) {
|
|
563
558
|
const keyStr = unknownToString(key);
|
|
564
|
-
console.warn(
|
|
565
|
-
`IMap.withMutations: key not found: ${Result.isOk(keyStr) ? keyStr.value : '<error converting key to string>'}`,
|
|
566
|
-
);
|
|
559
|
+
console.warn(`IMap.withMutations: key not found: ${keyStr}`);
|
|
567
560
|
}
|
|
568
561
|
break;
|
|
569
562
|
}
|