ts-data-forge 14.10.0 → 14.10.1
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 +5 -5
- package/dist/array/impl/array-utils-element-access.d.mts.map +1 -1
- package/dist/array/impl/array-utils-element-access.mjs +4 -4
- package/dist/array/impl/array-utils-element-access.mjs.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-array-cast.d.mts +8 -8
- package/dist/array/impl/array-utils-length-bounded-array-cast.d.mts.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-array-cast.mjs +28 -28
- package/dist/array/impl/array-utils-length-bounded-array-cast.mjs.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-array-guard.d.mts.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-array-guard.mjs +4 -4
- package/dist/array/impl/array-utils-length-bounded-array-guard.mjs.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-tuple-cast.d.mts +3 -4
- package/dist/array/impl/array-utils-length-bounded-tuple-cast.d.mts.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-tuple-cast.mjs +28 -29
- package/dist/array/impl/array-utils-length-bounded-tuple-cast.mjs.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-tuple-guard.d.mts.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-tuple-guard.mjs +3 -3
- package/dist/array/impl/array-utils-length-bounded-tuple-guard.mjs.map +1 -1
- package/dist/array/impl/array-utils-modification.d.mts +1 -1
- package/dist/array/impl/array-utils-modification.d.mts.map +1 -1
- package/dist/array/impl/array-utils-modification.mjs +15 -15
- package/dist/array/impl/array-utils-modification.mjs.map +1 -1
- package/dist/array/impl/array-utils-reducing-value.d.mts.map +1 -1
- package/dist/array/impl/array-utils-reducing-value.mjs +12 -12
- package/dist/array/impl/array-utils-reducing-value.mjs.map +1 -1
- package/dist/array/impl/array-utils-search.d.mts.map +1 -1
- package/dist/array/impl/array-utils-search.mjs +56 -56
- package/dist/array/impl/array-utils-search.mjs.map +1 -1
- package/dist/array/impl/array-utils-slicing.d.mts.map +1 -1
- package/dist/array/impl/array-utils-slicing.mjs +3 -3
- package/dist/array/impl/array-utils-slicing.mjs.map +1 -1
- package/dist/array/impl/array-utils-transformation.d.mts +25 -25
- package/dist/array/impl/array-utils-transformation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-transformation.mjs +22 -22
- package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
- package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-validation.mjs +2 -2
- package/dist/array/impl/array-utils-validation.mjs.map +1 -1
- package/dist/collections/imap-mapped.d.mts +2 -2
- package/dist/collections/imap.d.mts +3 -4
- package/dist/collections/imap.d.mts.map +1 -1
- package/dist/collections/imap.mjs +0 -1
- package/dist/collections/imap.mjs.map +1 -1
- package/dist/collections/iset-mapped.d.mts +1 -2
- package/dist/collections/iset-mapped.d.mts.map +1 -1
- package/dist/collections/iset-mapped.mjs.map +1 -1
- package/dist/collections/iset.d.mts +4 -5
- package/dist/collections/iset.d.mts.map +1 -1
- package/dist/collections/iset.mjs +0 -1
- package/dist/collections/iset.mjs.map +1 -1
- package/dist/number/num.d.mts +2 -2
- package/dist/number/num.mjs.map +1 -1
- package/dist/object/object.d.mts +2 -2
- package/dist/others/fast-deep-equal.d.mts.map +1 -1
- package/dist/others/fast-deep-equal.mjs +10 -7
- package/dist/others/fast-deep-equal.mjs.map +1 -1
- package/dist/others/replace-equal-deep.d.mts +9 -9
- package/dist/others/replace-equal-deep.mjs +12 -12
- package/dist/others/replace-equal-deep.mjs.map +1 -1
- package/dist/string/str.d.mts +2 -2
- package/package.json +12 -12
- package/src/array/impl/array-utils-element-access.mts +11 -11
- package/src/array/impl/array-utils-length-bounded-array-cast.mts +69 -69
- package/src/array/impl/array-utils-length-bounded-array-guard.mts +21 -21
- package/src/array/impl/array-utils-length-bounded-array-guard.test.mts +2 -2
- package/src/array/impl/array-utils-length-bounded-tuple-cast.mts +64 -65
- package/src/array/impl/array-utils-length-bounded-tuple-guard.mts +15 -15
- package/src/array/impl/array-utils-modification.mts +54 -54
- package/src/array/impl/array-utils-reducing-value.mts +55 -55
- package/src/array/impl/array-utils-search.mts +114 -114
- package/src/array/impl/array-utils-slicing.mts +16 -16
- package/src/array/impl/array-utils-transformation.mts +96 -96
- package/src/array/impl/array-utils-transformation.test.mts +2 -2
- package/src/array/impl/array-utils-validation.mts +19 -19
- package/src/array/impl/array-utils-validation.test.mts +1 -1
- package/src/collections/imap-mapped.mts +2 -2
- package/src/collections/imap.mts +3 -4
- package/src/collections/imap.test.mts +1 -1
- package/src/collections/iset-mapped.mts +1 -2
- package/src/collections/iset.mts +4 -5
- package/src/collections/iset.test.mts +1 -1
- package/src/number/num.mts +10 -10
- package/src/object/object.mts +2 -2
- package/src/others/fast-deep-equal.mts +17 -10
- package/src/others/fast-deep-equal.test.mts +141 -29
- package/src/others/replace-equal-deep.mts +37 -37
- package/src/others/replace-equal-deep.test.mts +13 -7
- package/src/string/str.mts +2 -2
|
@@ -203,6 +203,18 @@ describe(fastDeepEqual, () => {
|
|
|
203
203
|
y: { toString: () => 'Hello world!' } as any,
|
|
204
204
|
expected: '=',
|
|
205
205
|
},
|
|
206
|
+
{
|
|
207
|
+
name: 'objects whose own `valueOf` and `toString` are not functions are compared by key',
|
|
208
|
+
x: { valueOf: 1, toString: 'a' } as any,
|
|
209
|
+
y: { valueOf: 1, toString: 'a' } as any,
|
|
210
|
+
expected: '=',
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: 'objects whose own `valueOf` that is not a function differs are not equal',
|
|
214
|
+
x: { valueOf: 1 } as any,
|
|
215
|
+
y: { valueOf: 2 } as any,
|
|
216
|
+
expected: '!=',
|
|
217
|
+
},
|
|
206
218
|
{
|
|
207
219
|
name: 'objects with `toString` functions returning different values are not equal',
|
|
208
220
|
x: { toString: () => 'Hello world!' } as any,
|
|
@@ -212,6 +224,54 @@ describe(fastDeepEqual, () => {
|
|
|
212
224
|
] as const satisfies readonly TestCase[])('$name', testFn);
|
|
213
225
|
});
|
|
214
226
|
|
|
227
|
+
describe('null-prototype objects', () => {
|
|
228
|
+
const withNullPrototype = (
|
|
229
|
+
entries: ReadonlyRecord<string, unknown>,
|
|
230
|
+
): unknown => Object.setPrototypeOf({ ...entries }, null);
|
|
231
|
+
|
|
232
|
+
const groupByParity = (xs: readonly number[]): unknown =>
|
|
233
|
+
Object.groupBy(xs, (x) => (x % 2 === 0 ? 'even' : 'odd'));
|
|
234
|
+
|
|
235
|
+
test.each([
|
|
236
|
+
{
|
|
237
|
+
name: 'empty null-prototype objects are equal',
|
|
238
|
+
x: Object.create(null),
|
|
239
|
+
y: Object.create(null),
|
|
240
|
+
expected: '=',
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'null-prototype objects with the same entries are equal',
|
|
244
|
+
x: withNullPrototype({ a: 1, b: [2] }),
|
|
245
|
+
y: withNullPrototype({ a: 1, b: [2] }),
|
|
246
|
+
expected: '=',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: 'null-prototype objects with different entries are not equal',
|
|
250
|
+
x: withNullPrototype({ a: 1 }),
|
|
251
|
+
y: withNullPrototype({ a: 2 }),
|
|
252
|
+
expected: '!=',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'a null-prototype object and a plain object are not equal',
|
|
256
|
+
x: Object.create(null),
|
|
257
|
+
y: {},
|
|
258
|
+
expected: '!=',
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: 'Object.groupBy results with the same groups are equal',
|
|
262
|
+
x: groupByParity([1, 2, 3]),
|
|
263
|
+
y: groupByParity([1, 2, 3]),
|
|
264
|
+
expected: '=',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'Object.groupBy results with different groups are not equal',
|
|
268
|
+
x: groupByParity([1, 2, 3]),
|
|
269
|
+
y: groupByParity([1, 2, 4]),
|
|
270
|
+
expected: '!=',
|
|
271
|
+
},
|
|
272
|
+
] as const satisfies readonly TestCase[])('$name', testFn);
|
|
273
|
+
});
|
|
274
|
+
|
|
215
275
|
describe('arrays', () => {
|
|
216
276
|
test.each([
|
|
217
277
|
{ name: 'two empty arrays are equal', x: [], y: [], expected: '=' },
|
|
@@ -382,13 +442,13 @@ describe(fastDeepEqual, () => {
|
|
|
382
442
|
test.each([
|
|
383
443
|
{
|
|
384
444
|
name: 'empty maps are equal',
|
|
385
|
-
x: new Map(),
|
|
386
|
-
y: new Map(),
|
|
445
|
+
x: new Map<unknown, unknown>(),
|
|
446
|
+
y: new Map<unknown, unknown>(),
|
|
387
447
|
expected: '=',
|
|
388
448
|
},
|
|
389
449
|
{
|
|
390
450
|
name: 'empty maps of different class are not equal',
|
|
391
|
-
x: new Map(),
|
|
451
|
+
x: new Map<unknown, unknown>(),
|
|
392
452
|
y: new MyMap(),
|
|
393
453
|
expected: '!=',
|
|
394
454
|
},
|
|
@@ -436,32 +496,44 @@ describe(fastDeepEqual, () => {
|
|
|
436
496
|
},
|
|
437
497
|
{
|
|
438
498
|
name: 'equal maps (same sub-keys)',
|
|
439
|
-
x: new Map
|
|
440
|
-
|
|
499
|
+
x: new Map<string, readonly ReadonlyMap<unknown, string>[]>([
|
|
500
|
+
['a', [createMap({ b: 'c' })]],
|
|
501
|
+
]),
|
|
502
|
+
y: new Map<string, readonly ReadonlyMap<unknown, string>[]>([
|
|
503
|
+
['a', [createMap({ b: 'c' })]],
|
|
504
|
+
]),
|
|
441
505
|
expected: '=',
|
|
442
506
|
},
|
|
443
507
|
{
|
|
444
508
|
name: 'not equal maps (different sub-key value)',
|
|
445
|
-
x: new Map
|
|
446
|
-
|
|
509
|
+
x: new Map<string, readonly ReadonlyMap<unknown, string>[]>([
|
|
510
|
+
['a', [createMap({ b: 'c' })]],
|
|
511
|
+
]),
|
|
512
|
+
y: new Map<string, readonly ReadonlyMap<unknown, string>[]>([
|
|
513
|
+
['a', [createMap({ b: 'd' })]],
|
|
514
|
+
]),
|
|
447
515
|
expected: '!=',
|
|
448
516
|
},
|
|
449
517
|
{
|
|
450
518
|
name: 'not equal maps (different sub-key)',
|
|
451
|
-
x: new Map
|
|
452
|
-
|
|
519
|
+
x: new Map<string, readonly ReadonlyMap<unknown, string>[]>([
|
|
520
|
+
['a', [createMap({ b: 'c' })]],
|
|
521
|
+
]),
|
|
522
|
+
y: new Map<string, readonly ReadonlyMap<unknown, string>[]>([
|
|
523
|
+
['a', [createMap({ c: 'c' })]],
|
|
524
|
+
]),
|
|
453
525
|
expected: '!=',
|
|
454
526
|
},
|
|
455
527
|
{
|
|
456
528
|
name: 'empty map and empty object are equal',
|
|
457
529
|
x: {},
|
|
458
|
-
y: new Map(),
|
|
530
|
+
y: new Map<unknown, unknown>(),
|
|
459
531
|
expected: '!=',
|
|
460
532
|
},
|
|
461
533
|
{
|
|
462
534
|
name: 'empty map and empty array are not equal',
|
|
463
535
|
x: [],
|
|
464
|
-
y: new Map(),
|
|
536
|
+
y: new Map<unknown, unknown>(),
|
|
465
537
|
expected: '!=',
|
|
466
538
|
},
|
|
467
539
|
{
|
|
@@ -514,13 +586,13 @@ describe(fastDeepEqual, () => {
|
|
|
514
586
|
test.each([
|
|
515
587
|
{
|
|
516
588
|
name: 'empty sets are equal',
|
|
517
|
-
x: new Set(),
|
|
518
|
-
y: new Set(),
|
|
589
|
+
x: new Set<unknown>(),
|
|
590
|
+
y: new Set<unknown>(),
|
|
519
591
|
expected: '=',
|
|
520
592
|
},
|
|
521
593
|
{
|
|
522
594
|
name: 'empty sets of different class are not equal',
|
|
523
|
-
x: new Set(),
|
|
595
|
+
x: new Set<unknown>(),
|
|
524
596
|
y: new MySet(),
|
|
525
597
|
expected: '!=',
|
|
526
598
|
},
|
|
@@ -575,13 +647,13 @@ describe(fastDeepEqual, () => {
|
|
|
575
647
|
{
|
|
576
648
|
name: 'empty set and empty object are not equal',
|
|
577
649
|
x: {},
|
|
578
|
-
y: new Set(),
|
|
650
|
+
y: new Set<unknown>(),
|
|
579
651
|
expected: '!=',
|
|
580
652
|
},
|
|
581
653
|
{
|
|
582
654
|
name: 'empty set and empty array are not equal',
|
|
583
655
|
x: [],
|
|
584
|
-
y: new Set(),
|
|
656
|
+
y: new Set<unknown>(),
|
|
585
657
|
expected: '!=',
|
|
586
658
|
},
|
|
587
659
|
{
|
|
@@ -598,7 +670,7 @@ describe(fastDeepEqual, () => {
|
|
|
598
670
|
},
|
|
599
671
|
{
|
|
600
672
|
name: 'set and pseudo set are not equal',
|
|
601
|
-
x: new Set(),
|
|
673
|
+
x: new Set<unknown>(),
|
|
602
674
|
y: {
|
|
603
675
|
constructor: Set,
|
|
604
676
|
size: 0,
|
|
@@ -702,15 +774,58 @@ describe(fastDeepEqual, () => {
|
|
|
702
774
|
'content',
|
|
703
775
|
);
|
|
704
776
|
|
|
705
|
-
|
|
706
|
-
// fastDeepEqual should skip _owner comparison and not cause infinite loop
|
|
707
|
-
// The result depends on whether other properties are equal
|
|
708
|
-
const result = fastDeepEqualUntyped(element1, element2);
|
|
709
|
-
|
|
710
|
-
// The important thing is that it doesn't throw or hang
|
|
711
|
-
assert.isBoolean(result);
|
|
777
|
+
assert.isTrue(fastDeepEqualUntyped(element1, element2));
|
|
712
778
|
});
|
|
713
779
|
|
|
780
|
+
test.each([
|
|
781
|
+
{
|
|
782
|
+
name: 'element-shaped objects with different _owner are equal',
|
|
783
|
+
x: {
|
|
784
|
+
$$typeof: Symbol.for('react.element'),
|
|
785
|
+
type: 'div',
|
|
786
|
+
key: null,
|
|
787
|
+
props: { className: 'x' },
|
|
788
|
+
_owner: { name: 'A' },
|
|
789
|
+
},
|
|
790
|
+
y: {
|
|
791
|
+
$$typeof: Symbol.for('react.element'),
|
|
792
|
+
type: 'div',
|
|
793
|
+
key: null,
|
|
794
|
+
props: { className: 'x' },
|
|
795
|
+
_owner: { name: 'B' },
|
|
796
|
+
},
|
|
797
|
+
expected: '=',
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
name: 'element-shaped objects with different props are not equal',
|
|
801
|
+
x: {
|
|
802
|
+
$$typeof: Symbol.for('react.element'),
|
|
803
|
+
type: 'div',
|
|
804
|
+
props: { className: 'x' },
|
|
805
|
+
_owner: null,
|
|
806
|
+
},
|
|
807
|
+
y: {
|
|
808
|
+
$$typeof: Symbol.for('react.element'),
|
|
809
|
+
type: 'div',
|
|
810
|
+
props: { className: 'y' },
|
|
811
|
+
_owner: null,
|
|
812
|
+
},
|
|
813
|
+
expected: '!=',
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
name: '_owner is compared on an object without $$typeof',
|
|
817
|
+
x: { _owner: { name: 'A' } },
|
|
818
|
+
y: { _owner: { name: 'B' } },
|
|
819
|
+
expected: '!=',
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
name: '_owner is compared when $$typeof is falsy',
|
|
823
|
+
x: { $$typeof: null, _owner: { name: 'A' } },
|
|
824
|
+
y: { $$typeof: null, _owner: { name: 'B' } },
|
|
825
|
+
expected: '!=',
|
|
826
|
+
},
|
|
827
|
+
] as const satisfies readonly TestCase[])('$name', testFn);
|
|
828
|
+
|
|
714
829
|
test('React elements with different props are not equal', () => {
|
|
715
830
|
const element1 = React.createElement('div', { className: 'test1' });
|
|
716
831
|
|
|
@@ -747,9 +862,7 @@ describe(fastDeepEqual, () => {
|
|
|
747
862
|
child.render();
|
|
748
863
|
}
|
|
749
864
|
|
|
750
|
-
|
|
751
|
-
// The important thing is it doesn't throw or hang
|
|
752
|
-
assert.isTrue(mut_renderCount >= 1);
|
|
865
|
+
assert.strictEqual(mut_renderCount, 1);
|
|
753
866
|
|
|
754
867
|
// Update with different props - should re-render
|
|
755
868
|
const differentProps = {
|
|
@@ -763,8 +876,7 @@ describe(fastDeepEqual, () => {
|
|
|
763
876
|
child.render();
|
|
764
877
|
}
|
|
765
878
|
|
|
766
|
-
|
|
767
|
-
assert.isTrue(mut_renderCount >= 2);
|
|
879
|
+
assert.strictEqual(mut_renderCount, 2);
|
|
768
880
|
});
|
|
769
881
|
});
|
|
770
882
|
});
|
|
@@ -7,18 +7,18 @@ import { fastDeepEqual } from './fast-deep-equal.mjs';
|
|
|
7
7
|
* replaced by that part of `current` (structural sharing). When the whole of
|
|
8
8
|
* `next` is deeply equal to `current`, `current` itself is returned, so an
|
|
9
9
|
* `Object.is` comparison of the result with `current` tells whether anything
|
|
10
|
-
* changed, and a reader that compares by reference sees a new object only
|
|
11
|
-
*
|
|
10
|
+
* changed, and a reader that compares by reference sees a new object only where
|
|
11
|
+
* the value really changed.
|
|
12
12
|
*
|
|
13
13
|
* Only plain objects and arrays are walked into. Anything else — a `Map`, a
|
|
14
14
|
* `Set`, a `Date`, a class instance — is compared whole with
|
|
15
15
|
* {@link fastDeepEqual} and kept or replaced whole.
|
|
16
16
|
*
|
|
17
17
|
* Array items are matched by index. Give `key` to match them by key instead:
|
|
18
|
-
* an item for which it returns something other than `undefined` is matched
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* an item for which it returns something other than `undefined` is matched with
|
|
19
|
+
* the item of `current` it returns the same key for, and an item it returns
|
|
20
|
+
* `undefined` for is still matched by index. The one `key` is used for the
|
|
21
|
+
* arrays at every depth.
|
|
22
22
|
*
|
|
23
23
|
* Neither argument is changed. The result shares objects with both, so it
|
|
24
24
|
* relies on neither being changed afterwards.
|
|
@@ -91,23 +91,29 @@ export const replaceEqualDeep = <T,>(
|
|
|
91
91
|
next: T,
|
|
92
92
|
options?: ReplaceEqualDeepOptions,
|
|
93
93
|
): T =>
|
|
94
|
-
// The walk sees `unknown`; what it returns is `current`, `next`, or a copy
|
|
95
|
-
//
|
|
94
|
+
// The walk sees `unknown`; what it returns is `current`, `next`, or a copy of
|
|
95
|
+
// `next`'s shape whose parts are parts of either, so it is a `T`.
|
|
96
96
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
97
97
|
replace(current, next, options?.key) as T;
|
|
98
98
|
|
|
99
99
|
/** Options of {@link replaceEqualDeep}. */
|
|
100
100
|
export type ReplaceEqualDeepOptions = Readonly<{
|
|
101
101
|
/**
|
|
102
|
-
* The key an array item is matched by. Items it returns `undefined` for
|
|
103
|
-
*
|
|
104
|
-
*
|
|
102
|
+
* The key an array item is matched by. Items it returns `undefined` for are
|
|
103
|
+
* matched by index. Keys are compared with `SameValueZero`, as `Map` compares
|
|
104
|
+
* them.
|
|
105
105
|
*/
|
|
106
106
|
key?: (item: unknown) => unknown;
|
|
107
107
|
}>;
|
|
108
108
|
|
|
109
109
|
type Key = (item: unknown) => unknown;
|
|
110
110
|
|
|
111
|
+
const isPlainArray = (value: unknown): value is readonly unknown[] =>
|
|
112
|
+
Array.isArray(value) && Object.getPrototypeOf(value) === Array.prototype;
|
|
113
|
+
|
|
114
|
+
const isPlainObject = (value: unknown): value is UnknownRecord =>
|
|
115
|
+
isRecord(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
116
|
+
|
|
111
117
|
const replace = (
|
|
112
118
|
current: unknown,
|
|
113
119
|
next: unknown,
|
|
@@ -123,9 +129,26 @@ const replace = (
|
|
|
123
129
|
? current
|
|
124
130
|
: next;
|
|
125
131
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
const indexByKey = (
|
|
133
|
+
items: readonly unknown[],
|
|
134
|
+
key: Key,
|
|
135
|
+
): ReadonlyMap<unknown, unknown> => {
|
|
136
|
+
const mut_byKey = new Map<unknown, unknown>();
|
|
137
|
+
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
const itemKey = key(item);
|
|
140
|
+
|
|
141
|
+
if (itemKey !== undefined && !mut_byKey.has(itemKey)) {
|
|
142
|
+
mut_byKey.set(itemKey, item);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return mut_byKey;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Each walk returns `current` when every part of the result is the same part of
|
|
150
|
+
// `current`, `next` when every part is the same part of `next` (nothing was
|
|
151
|
+
// reused, so no copy is needed), and the copy otherwise.
|
|
129
152
|
|
|
130
153
|
const replaceArray = (
|
|
131
154
|
current: readonly unknown[],
|
|
@@ -200,26 +223,3 @@ const replaceObject = (
|
|
|
200
223
|
|
|
201
224
|
// What an item of `next` is matched with when nothing of `current` is.
|
|
202
225
|
const absent: unique symbol = Symbol('absent');
|
|
203
|
-
|
|
204
|
-
const indexByKey = (
|
|
205
|
-
items: readonly unknown[],
|
|
206
|
-
key: Key,
|
|
207
|
-
): ReadonlyMap<unknown, unknown> => {
|
|
208
|
-
const mut_byKey = new Map<unknown, unknown>();
|
|
209
|
-
|
|
210
|
-
for (const item of items) {
|
|
211
|
-
const itemKey = key(item);
|
|
212
|
-
|
|
213
|
-
if (itemKey !== undefined && !mut_byKey.has(itemKey)) {
|
|
214
|
-
mut_byKey.set(itemKey, item);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return mut_byKey;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
const isPlainArray = (value: unknown): value is readonly unknown[] =>
|
|
222
|
-
Array.isArray(value) && Object.getPrototypeOf(value) === Array.prototype;
|
|
223
|
-
|
|
224
|
-
const isPlainObject = (value: unknown): value is UnknownRecord =>
|
|
225
|
-
isRecord(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
@@ -228,11 +228,17 @@ describe(replaceEqualDeep, () => {
|
|
|
228
228
|
});
|
|
229
229
|
|
|
230
230
|
test('compares a Map, a Set, a Date or a class instance whole', () => {
|
|
231
|
-
const map = new Map([['a', { x: 1 }]]);
|
|
231
|
+
const map = new Map<string, Readonly<{ x: number }>>([['a', { x: 1 }]]);
|
|
232
232
|
|
|
233
|
-
assert.strictEqual(
|
|
233
|
+
assert.strictEqual(
|
|
234
|
+
replaceEqualDeep(
|
|
235
|
+
map,
|
|
236
|
+
new Map<string, Readonly<{ x: number }>>([['a', { x: 1 }]]),
|
|
237
|
+
),
|
|
238
|
+
map,
|
|
239
|
+
);
|
|
234
240
|
|
|
235
|
-
const changedMap = new Map([
|
|
241
|
+
const changedMap = new Map<string, Readonly<{ x: number }>>([
|
|
236
242
|
['a', { x: 1 }],
|
|
237
243
|
['b', { x: 2 }],
|
|
238
244
|
]);
|
|
@@ -244,9 +250,9 @@ describe(replaceEqualDeep, () => {
|
|
|
244
250
|
// Nothing inside is reused.
|
|
245
251
|
assert.notStrictEqual(sharedMap.get('a'), map.get('a'));
|
|
246
252
|
|
|
247
|
-
const set = new Set([1, 2]);
|
|
253
|
+
const set = new Set<number>([1, 2]);
|
|
248
254
|
|
|
249
|
-
assert.strictEqual(replaceEqualDeep(set, new Set([1, 2])), set);
|
|
255
|
+
assert.strictEqual(replaceEqualDeep(set, new Set<number>([1, 2])), set);
|
|
250
256
|
|
|
251
257
|
const date = new Date(0);
|
|
252
258
|
|
|
@@ -282,12 +288,12 @@ describe(replaceEqualDeep, () => {
|
|
|
282
288
|
});
|
|
283
289
|
|
|
284
290
|
test('reuses a Map compared whole inside a plain object', () => {
|
|
285
|
-
const map = new Map([['a', 1]]);
|
|
291
|
+
const map = new Map<string, number>([['a', 1]]);
|
|
286
292
|
|
|
287
293
|
const current = { map, count: 1 } as const;
|
|
288
294
|
|
|
289
295
|
const shared = replaceEqualDeep(current, {
|
|
290
|
-
map: new Map([['a', 1]]),
|
|
296
|
+
map: new Map<string, number>([['a', 1]]),
|
|
291
297
|
count: 2,
|
|
292
298
|
});
|
|
293
299
|
|
package/src/string/str.mts
CHANGED
|
@@ -522,8 +522,8 @@ export namespace Str {
|
|
|
522
522
|
* `NonEmptyString` is an alias of `MinLengthString<1>`, so this is the
|
|
523
523
|
* length-1 specialization of {@link asMinLengthString}. Use this instead of
|
|
524
524
|
* writing `s as unknown as NonEmptyString`: the length is verified at
|
|
525
|
-
* runtime, and the original string type (e.g. literal types) is preserved
|
|
526
|
-
*
|
|
525
|
+
* runtime, and the original string type (e.g. literal types) is preserved via
|
|
526
|
+
* intersection.
|
|
527
527
|
*
|
|
528
528
|
* @example
|
|
529
529
|
*
|