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.
Files changed (88) hide show
  1. package/README.md +5 -5
  2. package/dist/array/impl/array-utils-element-access.d.mts.map +1 -1
  3. package/dist/array/impl/array-utils-element-access.mjs +4 -4
  4. package/dist/array/impl/array-utils-element-access.mjs.map +1 -1
  5. package/dist/array/impl/array-utils-length-bounded-array-cast.d.mts +8 -8
  6. package/dist/array/impl/array-utils-length-bounded-array-cast.d.mts.map +1 -1
  7. package/dist/array/impl/array-utils-length-bounded-array-cast.mjs +28 -28
  8. package/dist/array/impl/array-utils-length-bounded-array-cast.mjs.map +1 -1
  9. package/dist/array/impl/array-utils-length-bounded-array-guard.d.mts.map +1 -1
  10. package/dist/array/impl/array-utils-length-bounded-array-guard.mjs +4 -4
  11. package/dist/array/impl/array-utils-length-bounded-array-guard.mjs.map +1 -1
  12. package/dist/array/impl/array-utils-length-bounded-tuple-cast.d.mts +3 -4
  13. package/dist/array/impl/array-utils-length-bounded-tuple-cast.d.mts.map +1 -1
  14. package/dist/array/impl/array-utils-length-bounded-tuple-cast.mjs +28 -29
  15. package/dist/array/impl/array-utils-length-bounded-tuple-cast.mjs.map +1 -1
  16. package/dist/array/impl/array-utils-length-bounded-tuple-guard.d.mts.map +1 -1
  17. package/dist/array/impl/array-utils-length-bounded-tuple-guard.mjs +3 -3
  18. package/dist/array/impl/array-utils-length-bounded-tuple-guard.mjs.map +1 -1
  19. package/dist/array/impl/array-utils-modification.d.mts +1 -1
  20. package/dist/array/impl/array-utils-modification.d.mts.map +1 -1
  21. package/dist/array/impl/array-utils-modification.mjs +15 -15
  22. package/dist/array/impl/array-utils-modification.mjs.map +1 -1
  23. package/dist/array/impl/array-utils-reducing-value.d.mts.map +1 -1
  24. package/dist/array/impl/array-utils-reducing-value.mjs +12 -12
  25. package/dist/array/impl/array-utils-reducing-value.mjs.map +1 -1
  26. package/dist/array/impl/array-utils-search.d.mts.map +1 -1
  27. package/dist/array/impl/array-utils-search.mjs +56 -56
  28. package/dist/array/impl/array-utils-search.mjs.map +1 -1
  29. package/dist/array/impl/array-utils-slicing.d.mts.map +1 -1
  30. package/dist/array/impl/array-utils-slicing.mjs +3 -3
  31. package/dist/array/impl/array-utils-slicing.mjs.map +1 -1
  32. package/dist/array/impl/array-utils-transformation.d.mts +25 -25
  33. package/dist/array/impl/array-utils-transformation.d.mts.map +1 -1
  34. package/dist/array/impl/array-utils-transformation.mjs +22 -22
  35. package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
  36. package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
  37. package/dist/array/impl/array-utils-validation.mjs +2 -2
  38. package/dist/array/impl/array-utils-validation.mjs.map +1 -1
  39. package/dist/collections/imap-mapped.d.mts +2 -2
  40. package/dist/collections/imap.d.mts +3 -4
  41. package/dist/collections/imap.d.mts.map +1 -1
  42. package/dist/collections/imap.mjs +0 -1
  43. package/dist/collections/imap.mjs.map +1 -1
  44. package/dist/collections/iset-mapped.d.mts +1 -2
  45. package/dist/collections/iset-mapped.d.mts.map +1 -1
  46. package/dist/collections/iset-mapped.mjs.map +1 -1
  47. package/dist/collections/iset.d.mts +4 -5
  48. package/dist/collections/iset.d.mts.map +1 -1
  49. package/dist/collections/iset.mjs +0 -1
  50. package/dist/collections/iset.mjs.map +1 -1
  51. package/dist/number/num.d.mts +2 -2
  52. package/dist/number/num.mjs.map +1 -1
  53. package/dist/object/object.d.mts +2 -2
  54. package/dist/others/fast-deep-equal.d.mts.map +1 -1
  55. package/dist/others/fast-deep-equal.mjs +10 -7
  56. package/dist/others/fast-deep-equal.mjs.map +1 -1
  57. package/dist/others/replace-equal-deep.d.mts +9 -9
  58. package/dist/others/replace-equal-deep.mjs +12 -12
  59. package/dist/others/replace-equal-deep.mjs.map +1 -1
  60. package/dist/string/str.d.mts +2 -2
  61. package/package.json +12 -12
  62. package/src/array/impl/array-utils-element-access.mts +11 -11
  63. package/src/array/impl/array-utils-length-bounded-array-cast.mts +69 -69
  64. package/src/array/impl/array-utils-length-bounded-array-guard.mts +21 -21
  65. package/src/array/impl/array-utils-length-bounded-array-guard.test.mts +2 -2
  66. package/src/array/impl/array-utils-length-bounded-tuple-cast.mts +64 -65
  67. package/src/array/impl/array-utils-length-bounded-tuple-guard.mts +15 -15
  68. package/src/array/impl/array-utils-modification.mts +54 -54
  69. package/src/array/impl/array-utils-reducing-value.mts +55 -55
  70. package/src/array/impl/array-utils-search.mts +114 -114
  71. package/src/array/impl/array-utils-slicing.mts +16 -16
  72. package/src/array/impl/array-utils-transformation.mts +96 -96
  73. package/src/array/impl/array-utils-transformation.test.mts +2 -2
  74. package/src/array/impl/array-utils-validation.mts +19 -19
  75. package/src/array/impl/array-utils-validation.test.mts +1 -1
  76. package/src/collections/imap-mapped.mts +2 -2
  77. package/src/collections/imap.mts +3 -4
  78. package/src/collections/imap.test.mts +1 -1
  79. package/src/collections/iset-mapped.mts +1 -2
  80. package/src/collections/iset.mts +4 -5
  81. package/src/collections/iset.test.mts +1 -1
  82. package/src/number/num.mts +10 -10
  83. package/src/object/object.mts +2 -2
  84. package/src/others/fast-deep-equal.mts +17 -10
  85. package/src/others/fast-deep-equal.test.mts +141 -29
  86. package/src/others/replace-equal-deep.mts +37 -37
  87. package/src/others/replace-equal-deep.test.mts +13 -7
  88. package/src/string/str.mts +2 -2
@@ -48,8 +48,8 @@ export function map<const Ar extends readonly unknown[] & UnknownBrand, B>(
48
48
  mapFn: (a: Ar[number], index: ArrayIndex<Ar>) => B,
49
49
  ): ChangeArrayElement<Ar, B>;
50
50
 
51
- // A plain array or tuple carries no length brand, so the homomorphic mapping
52
- // is the whole answer. Stating it directly — rather than going through
51
+ // A plain array or tuple carries no length brand, so the homomorphic mapping is
52
+ // the whole answer. Stating it directly — rather than going through
53
53
  // `ChangeArrayElement`, whose `HasLengthConstraint` test a *generic* `Ar`
54
54
  // cannot decide — is what keeps `map` usable inside a function that is itself
55
55
  // generic over the tuple: a deferred conditional is not assignable to the
@@ -68,8 +68,8 @@ export function map<const Ar extends readonly unknown[], B>(
68
68
  //
69
69
  // Spelled as an intersection of two function types rather than one object type
70
70
  // carrying two call signatures — the `convert-to-readonly` codemod wraps an
71
- // inline object type in `Readonly<…>`, and a mapped type drops call
72
- // signatures, which would leave the curried form with none at all.
71
+ // inline object type in `Readonly<…>`, and a mapped type drops call signatures,
72
+ // which would leave the curried form with none at all.
73
73
  export function map<A, B>(
74
74
  mapFn: (a: A, index: SizeType.Arr) => B,
75
75
  ): (<const Ar extends readonly A[] & UnknownBrand>(
@@ -175,26 +175,6 @@ export function scan<E, S>(
175
175
  }
176
176
  }
177
177
 
178
- const scanImpl = <E, S>(
179
- array: readonly E[],
180
- reducer: (accumulator: S, currentValue: E, currentIndex: SizeType.Arr) => S,
181
- init: S,
182
- ): NonEmptyTuple<S> => {
183
- const mut_result = castMutable<NonEmptyTuple<S>>(
184
- newArray<PositiveUint32, S>(asPositiveUint32(array.length + 1), init),
185
- );
186
-
187
- let mut_acc = init;
188
-
189
- for (const [index, value] of array.entries()) {
190
- mut_acc = reducer(mut_acc, value, asUint32(index));
191
-
192
- mut_result[index + 1] = mut_acc;
193
- }
194
-
195
- return mut_result;
196
- };
197
-
198
178
  /**
199
179
  * Reverses an array.
200
180
  *
@@ -277,16 +257,6 @@ export function toSorted(
277
257
  );
278
258
  }
279
259
 
280
- /** The result of sorting `Ar`: same elements, same length, same bounds. */
281
- type SortedResult<Ar extends readonly unknown[]> =
282
- HasLengthConstraint<Ar> extends true
283
- ? ChangeArrayElement<Ar, Ar[number]>
284
- : IsFixedLengthList<Ar> extends true
285
- ? FixedLengthTuple<Ar['length'], Ar[number]>
286
- : Ar extends NonEmptyTuple<unknown>
287
- ? NonEmptyArray<Ar[number]>
288
- : readonly Ar[number][];
289
-
290
260
  /**
291
261
  * Sorts an array by a mapped value.
292
262
  *
@@ -426,11 +396,6 @@ export function filter<E>(
426
396
  }
427
397
  }
428
398
 
429
- const filterImpl = <E,>(
430
- array: readonly E[],
431
- predicate: (a: E, index: SizeType.Arr) => boolean,
432
- ): readonly E[] => array.filter((a, i) => predicate(a, asUint32(i)));
433
-
434
399
  /**
435
400
  * Filters an array by excluding elements for which the predicate returns true.
436
401
  *
@@ -474,11 +439,6 @@ export function filterNot<E>(
474
439
  }
475
440
  }
476
441
 
477
- const filterNotImpl = <E,>(
478
- array: readonly E[],
479
- predicate: (a: E, index: SizeType.Arr) => boolean,
480
- ): readonly E[] => array.filter((a, i) => !predicate(a, asUint32(i)));
481
-
482
442
  /**
483
443
  * Creates a new array with unique elements.
484
444
  *
@@ -615,11 +575,6 @@ export function flat<E, D extends SafeUintWithSmallInt = 1>(
615
575
  }
616
576
  }
617
577
 
618
- const flatImpl = (
619
- array: readonly unknown[],
620
- depth?: SafeUintWithSmallInt,
621
- ): readonly unknown[] => array.flat(depth);
622
-
623
578
  /**
624
579
  * Maps each element and flattens the result.
625
580
  *
@@ -734,37 +689,6 @@ export function partition<
734
689
  }
735
690
  }
736
691
 
737
- /**
738
- * Element type of {@link partition}'s result: a chunk holds between 1 and `N`
739
- * elements. The lower bound is what makes this worth spelling out — `partition`
740
- * never emits an empty chunk, so indexed access into a chunk is not
741
- * `undefined` at position `0`, unlike with a plain `readonly E[]`.
742
- *
743
- * Falls back to {@link NonEmptyArray} when `N` is not a bound the brand can
744
- * express, which keeps that lower bound in every case.
745
- */
746
- type PartitionChunk<N, E> = N extends SupportedLength
747
- ? BoundedLengthArray<1, N, E>
748
- : NonEmptyArray<E>;
749
-
750
- const partitionImpl = <E, N extends WithSmallInt<PositiveInt & SizeType.Arr>>(
751
- array: readonly E[],
752
- chunkSize: N,
753
- ): readonly (readonly E[])[] =>
754
- chunkSize < 2
755
- ? ([] as const)
756
- : // eslint-disable-next-line total-functions/no-partial-division
757
- seq(asUint32(Math.ceil(array.length / chunkSize))).map((i: Uint32) =>
758
- array.slice(chunkSize * i, chunkSize * (i + 1)),
759
- );
760
-
761
- /**
762
- * Alias for `partition`.
763
- *
764
- * @see {@link partition}
765
- */
766
- export const chunk = partition;
767
-
768
692
  /**
769
693
  * Concatenates two arrays.
770
694
  *
@@ -805,22 +729,6 @@ export const concat = <
805
729
  // eslint-disable-next-line total-functions/no-unsafe-type-assertion
806
730
  [...array1, ...array2] as never;
807
731
 
808
- /**
809
- * Return type of {@link concat}: the concatenated tuple `readonly [...Ar1,
810
- * ...Ar2]`, additionally branded as {@link NonEmptyArray} when either input is
811
- * a non-empty tuple (a plain non-empty tuple or a branded `NonEmptyArray`,
812
- * both of which extend `NonEmptyTuple<unknown>`).
813
- */
814
- type ConcatResult<
815
- Ar1 extends readonly unknown[],
816
- Ar2 extends readonly unknown[],
817
- > =
818
- Ar1 extends NonEmptyTuple<unknown>
819
- ? readonly [...Ar1, ...Ar2] & NonEmptyArray<Ar1[number] | Ar2[number]>
820
- : Ar2 extends NonEmptyTuple<unknown>
821
- ? readonly [...Ar1, ...Ar2] & NonEmptyArray<Ar1[number] | Ar2[number]>
822
- : readonly [...Ar1, ...Ar2];
823
-
824
732
  /**
825
733
  * Groups elements by a key derived from each element.
826
734
  *
@@ -985,3 +893,95 @@ export const cartesianProduct = <T,>(
985
893
 
986
894
  return first.flatMap((item) => restProduct.map((prod) => [item, ...prod]));
987
895
  };
896
+
897
+ const scanImpl = <E, S>(
898
+ array: readonly E[],
899
+ reducer: (accumulator: S, currentValue: E, currentIndex: SizeType.Arr) => S,
900
+ init: S,
901
+ ): NonEmptyTuple<S> => {
902
+ const mut_result = castMutable<NonEmptyTuple<S>>(
903
+ newArray<PositiveUint32, S>(asPositiveUint32(array.length + 1), init),
904
+ );
905
+
906
+ let mut_acc = init;
907
+
908
+ for (const [index, value] of array.entries()) {
909
+ mut_acc = reducer(mut_acc, value, asUint32(index));
910
+
911
+ mut_result[index + 1] = mut_acc;
912
+ }
913
+
914
+ return mut_result;
915
+ };
916
+
917
+ /** The result of sorting `Ar`: same elements, same length, same bounds. */
918
+ type SortedResult<Ar extends readonly unknown[]> =
919
+ HasLengthConstraint<Ar> extends true
920
+ ? ChangeArrayElement<Ar, Ar[number]>
921
+ : IsFixedLengthList<Ar> extends true
922
+ ? FixedLengthTuple<Ar['length'], Ar[number]>
923
+ : Ar extends NonEmptyTuple<unknown>
924
+ ? NonEmptyArray<Ar[number]>
925
+ : readonly Ar[number][];
926
+
927
+ const filterImpl = <E,>(
928
+ array: readonly E[],
929
+ predicate: (a: E, index: SizeType.Arr) => boolean,
930
+ ): readonly E[] => array.filter((a, i) => predicate(a, asUint32(i)));
931
+
932
+ const filterNotImpl = <E,>(
933
+ array: readonly E[],
934
+ predicate: (a: E, index: SizeType.Arr) => boolean,
935
+ ): readonly E[] => array.filter((a, i) => !predicate(a, asUint32(i)));
936
+
937
+ const flatImpl = (
938
+ array: readonly unknown[],
939
+ depth?: SafeUintWithSmallInt,
940
+ ): readonly unknown[] => array.flat(depth);
941
+
942
+ /**
943
+ * Element type of {@link partition}'s result: a chunk holds between 1 and `N`
944
+ * elements. The lower bound is what makes this worth spelling out — `partition`
945
+ * never emits an empty chunk, so indexed access into a chunk is not `undefined`
946
+ * at position `0`, unlike with a plain `readonly E[]`.
947
+ *
948
+ * Falls back to {@link NonEmptyArray} when `N` is not a bound the brand can
949
+ * express, which keeps that lower bound in every case.
950
+ */
951
+ type PartitionChunk<N, E> = N extends SupportedLength
952
+ ? BoundedLengthArray<1, N, E>
953
+ : NonEmptyArray<E>;
954
+
955
+ const partitionImpl = <E, N extends WithSmallInt<PositiveInt & SizeType.Arr>>(
956
+ array: readonly E[],
957
+ chunkSize: N,
958
+ ): readonly (readonly E[])[] =>
959
+ chunkSize < 2
960
+ ? ([] as const)
961
+ : // eslint-disable-next-line total-functions/no-partial-division
962
+ seq(asUint32(Math.ceil(array.length / chunkSize))).map((i: Uint32) =>
963
+ array.slice(chunkSize * i, chunkSize * (i + 1)),
964
+ );
965
+
966
+ /**
967
+ * Alias for `partition`.
968
+ *
969
+ * @see {@link partition}
970
+ */
971
+ export const chunk = partition;
972
+
973
+ /**
974
+ * Return type of {@link concat}: the concatenated tuple `readonly [...Ar1,
975
+ * ...Ar2]`, additionally branded as {@link NonEmptyArray} when either input is
976
+ * a non-empty tuple (a plain non-empty tuple or a branded `NonEmptyArray`, both
977
+ * of which extend `NonEmptyTuple<unknown>`).
978
+ */
979
+ type ConcatResult<
980
+ Ar1 extends readonly unknown[],
981
+ Ar2 extends readonly unknown[],
982
+ > =
983
+ Ar1 extends NonEmptyTuple<unknown>
984
+ ? readonly [...Ar1, ...Ar2] & NonEmptyArray<Ar1[number] | Ar2[number]>
985
+ : Ar2 extends NonEmptyTuple<unknown>
986
+ ? readonly [...Ar1, ...Ar2] & NonEmptyArray<Ar1[number] | Ar2[number]>
987
+ : readonly [...Ar1, ...Ar2];
@@ -71,8 +71,8 @@ describe('Arr transformations', () => {
71
71
 
72
72
  test('should preserve the tuple shape under a generic parameter', () => {
73
73
  // A caller that is itself generic over the tuple cannot resolve a
74
- // conditional return type, so `map` has to report the homomorphic
75
- // mapping directly for a parameter that carries no length brand.
74
+ // conditional return type, so `map` has to report the homomorphic mapping
75
+ // directly for a parameter that carries no length brand.
76
76
  const mapValues = <const T extends readonly Readonly<{ v: unknown }>[]>(
77
77
  boxes: T,
78
78
  ): Readonly<{ [K in keyof T]: unknown }> => map(boxes, (b) => b.v);
@@ -63,11 +63,11 @@ type Cast<A, B> = A extends B ? A : never;
63
63
  */
64
64
  // Type guard overloads - narrow the entire array type.
65
65
  //
66
- // Both narrowed types are intersected with `Ar`: a type predicate's type has
67
- // to be assignable to the parameter's type, and neither the deferred
68
- // conditional `ChangeArrayElement<Ar, S>` nor the mapped type over a generic
69
- // `Ar` is (TS2677). The intersection also keeps whatever the caller already
70
- // knew about the array — a branded five-tuple stays a five-tuple.
66
+ // Both narrowed types are intersected with `Ar`: a type predicate's type has to
67
+ // be assignable to the parameter's type, and neither the deferred conditional
68
+ // `ChangeArrayElement<Ar, S>` nor the mapped type over a generic `Ar` is
69
+ // (TS2677). The intersection also keeps whatever the caller already knew about
70
+ // the array — a branded five-tuple stays a five-tuple.
71
71
  export function every<
72
72
  const Ar extends readonly unknown[] & UnknownBrand,
73
73
  S extends Ar[number],
@@ -76,8 +76,8 @@ export function every<
76
76
  predicate: (a: Ar[number], index: ArrayIndex<Ar>) => a is S,
77
77
  ): array is ChangeArrayElement<Ar, S> & Ar;
78
78
 
79
- // A plain array or tuple carries no length brand, so the homomorphic mapping
80
- // is the whole answer — same reasoning as in `map`.
79
+ // A plain array or tuple carries no length brand, so the homomorphic mapping is
80
+ // the whole answer — same reasoning as in `map`.
81
81
  export function every<
82
82
  const Ar extends readonly unknown[],
83
83
  S extends Ar[number],
@@ -98,8 +98,8 @@ export function every<const Ar extends readonly unknown[]>(
98
98
  // returned function*, not two overloads of `every` itself: both curried
99
99
  // signatures take the same single predicate, so overload resolution would
100
100
  // always pick the first and the second would be dead — which is what left a
101
- // plain `readonly unknown[]` unable to satisfy the `UnknownBrand` constraint
102
- // at the call site of the returned guard.
101
+ // plain `readonly unknown[]` unable to satisfy the `UnknownBrand` constraint at
102
+ // the call site of the returned guard.
103
103
  //
104
104
  // The intersection is also what keeps the group compatible with the
105
105
  // implementation signature. A lone `<Ar extends readonly E[] & UnknownBrand>`
@@ -136,11 +136,6 @@ export function every<E>(
136
136
  }
137
137
  }
138
138
 
139
- const everyImpl = <E,>(
140
- array: readonly E[],
141
- predicate: (a: E, index: SizeType.Arr) => boolean,
142
- ): boolean => array.every((a, i) => predicate(a, asUint32(i)));
143
-
144
139
  /**
145
140
  * Tests whether at least one element in an array passes a test implemented by a predicate.
146
141
  *
@@ -186,11 +181,6 @@ export function some<E>(
186
181
  }
187
182
  }
188
183
 
189
- const someImpl = <E,>(
190
- array: readonly E[],
191
- predicate: (a: E, index: SizeType.Arr) => boolean,
192
- ): boolean => array.some((a, i) => predicate(a, asUint32(i)));
193
-
194
184
  /**
195
185
  * Checks if an index is within the valid range of an array.
196
186
  *
@@ -212,3 +202,13 @@ export const indexIsInRange = <E,>(
212
202
  array: readonly E[],
213
203
  index: SizeType.ArgArr,
214
204
  ): boolean => Num.isInRange(0, array.length)(index);
205
+
206
+ const everyImpl = <E,>(
207
+ array: readonly E[],
208
+ predicate: (a: E, index: SizeType.Arr) => boolean,
209
+ ): boolean => array.every((a, i) => predicate(a, asUint32(i)));
210
+
211
+ const someImpl = <E,>(
212
+ array: readonly E[],
213
+ predicate: (a: E, index: SizeType.Arr) => boolean,
214
+ ): boolean => array.some((a, i) => predicate(a, asUint32(i)));
@@ -39,7 +39,7 @@ describe('Arr validations', () => {
39
39
 
40
40
  assert.isFalse(isArray({}));
41
41
 
42
- assert.isFalse(isArray(new Set()));
42
+ assert.isFalse(isArray(new Set<unknown>()));
43
43
  });
44
44
 
45
45
  test('should refine union types correctly', () => {
@@ -125,8 +125,8 @@ type IMapMappedInterface<K, V, KM extends MapSetKeyType> = Readonly<{
125
125
  // Reducing a value
126
126
 
127
127
  /**
128
- * Checks if all elements in the map satisfy a predicate. Also supports a
129
- * type predicate overload that narrows the type of values in the map if the
128
+ * Checks if all elements in the map satisfy a predicate. Also supports a type
129
+ * predicate overload that narrows the type of values in the map if the
130
130
  * predicate returns true for all elements.
131
131
  *
132
132
  * @example
@@ -96,8 +96,8 @@ type IMapInterface<K extends MapSetKeyType, V> = Readonly<{
96
96
  // Reducing a value
97
97
 
98
98
  /**
99
- * Checks if all elements in the map satisfy a predicate. Also supports a
100
- * type predicate overload that narrows the type of values in the map if the
99
+ * Checks if all elements in the map satisfy a predicate. Also supports a type
100
+ * predicate overload that narrows the type of values in the map if the
101
101
  * predicate returns true for all elements.
102
102
  *
103
103
  * @example
@@ -656,8 +656,7 @@ export namespace IMap {
656
656
  *
657
657
  * Two IMaps are considered equal if they have the same size and contain
658
658
  * exactly the same key-value pairs. The order of entries does not matter for
659
- * equality comparison. Values are compared using JavaScript's `===`
660
- * operator.
659
+ * equality comparison. Values are compared using JavaScript's `===` operator.
661
660
  *
662
661
  * **Performance:** O(n) where n is the size of the smaller map.
663
662
  *
@@ -81,7 +81,7 @@ describe('IMap.create', () => {
81
81
  });
82
82
 
83
83
  test('should create map from JavaScript Map', () => {
84
- const jsMap = new Map([
84
+ const jsMap = new Map<string, number>([
85
85
  ['a', 1],
86
86
  ['b', 2],
87
87
  ]);
@@ -138,8 +138,7 @@ type ISetMappedInterface<K, KM extends MapSetKeyType> = Readonly<{
138
138
 
139
139
  /**
140
140
  * Checks if all elements in the set satisfy a type predicate. Narrows the
141
- * type of elements in the set if the predicate returns true for all
142
- * elements.
141
+ * type of elements in the set if the predicate returns true for all elements.
143
142
  *
144
143
  *
145
144
  * @example
@@ -89,9 +89,9 @@ type ISetInterface<K extends MapSetKeyType> = Readonly<{
89
89
  // Reducing a value
90
90
 
91
91
  /**
92
- * Checks if all elements in the set satisfy a predicate. Also supports a
93
- * type predicate overload that narrows the type of elements in the set if
94
- * the predicate returns true for all elements.
92
+ * Checks if all elements in the set satisfy a predicate. Also supports a type
93
+ * predicate overload that narrows the type of elements in the set if the
94
+ * predicate returns true for all elements.
95
95
  *
96
96
  * @example
97
97
  *
@@ -539,8 +539,7 @@ export namespace ISet {
539
539
  *
540
540
  * This factory function accepts any iterable of elements, including arrays,
541
541
  * JavaScript Sets, other ISets, or custom iterables. Duplicate elements in
542
- * the input iterable will be automatically deduplicated in the resulting
543
- * set.
542
+ * the input iterable will be automatically deduplicated in the resulting set.
544
543
  *
545
544
  * **Performance:** O(n) where n is the number of elements in the iterable.
546
545
  *
@@ -39,7 +39,7 @@ describe('ISet.create', () => {
39
39
  });
40
40
 
41
41
  test('should create set from JavaScript Set', () => {
42
- const jsSet = new Set(['a', 'b', 'c']);
42
+ const jsSet = new Set<string>(['a', 'b', 'c']);
43
43
 
44
44
  const set = ISet.create(jsSet);
45
45
 
@@ -120,8 +120,8 @@ export namespace Num {
120
120
  *
121
121
  * assert.strictEqual(Result.unwrapOk(Num.safeParseInt(' ')), undefined);
122
122
  *
123
- * // A value that overflows to Infinity is not an integer either, even
124
- * // though native `parseInt` reads a 1 out of it.
123
+ * // A value that overflows to Infinity is not an integer either, even though
124
+ * // native `parseInt` reads a 1 out of it.
125
125
  *
126
126
  * assert.strictEqual(Number.parseInt('1e400', 10), 1);
127
127
  *
@@ -135,10 +135,10 @@ export namespace Num {
135
135
  */
136
136
  export const safeParseInt = (s: string): Result<Int, Error> =>
137
137
  // The parsing itself is `SafeNumber.parseInteger` (Sumi D-49 (c)): one
138
- // implementation, in the package that owns the standard-library
139
- // boundary. What stays here is this function's own contract — the `Int`
140
- // brand on the success side, and an `Error` rather than a tagged record
141
- // on the failure side.
138
+ // implementation, in the package that owns the standard-library boundary.
139
+ // What stays here is this function's own contract — the `Int` brand on the
140
+ // success side, and an `Error` rather than a tagged record on the failure
141
+ // side.
142
142
  Result.match(SafeNumber.parseInteger(s), {
143
143
  // eslint-disable-next-line total-functions/no-unsafe-type-assertion
144
144
  ok: (n) => Result.ok(n as Int),
@@ -213,10 +213,10 @@ export namespace Num {
213
213
  */
214
214
  export const safeParseFloat = (s: string): Result<FiniteNumber, Error> =>
215
215
  // The parsing itself is `SafeNumber.parse` (Sumi D-49 (c)): one
216
- // implementation, in the package that owns the standard-library
217
- // boundary. What stays here is this function's own contract — the
218
- // `FiniteNumber` brand on the success side, and an `Error` rather than a
219
- // tagged record on the failure side.
216
+ // implementation, in the package that owns the standard-library boundary.
217
+ // What stays here is this function's own contract — the `FiniteNumber`
218
+ // brand on the success side, and an `Error` rather than a tagged record on
219
+ // the failure side.
220
220
  Result.match(SafeNumber.parse(s), {
221
221
  // eslint-disable-next-line total-functions/no-unsafe-type-assertion
222
222
  ok: (n) => Result.ok(n as FiniteNumber),
@@ -337,8 +337,8 @@ export namespace Obj {
337
337
  * This is what `Object.fromEntries(Object.entries(record).map(...))` cannot
338
338
  * express: an entries array carries no evidence that it still covers every
339
339
  * key of the record it came from, so its element type is all the return type
340
- * can be built from. Going through a mapped type instead states the
341
- * invariant that a value-only transform preserves the key set.
340
+ * can be built from. Going through a mapped type instead states the invariant
341
+ * that a value-only transform preserves the key set.
342
342
  *
343
343
  * **Key handling**: keys reach the callback the way `Object.entries` yields
344
344
  * them, so a numeric key `1` arrives as `'1'`. Symbol-keyed properties are
@@ -67,12 +67,20 @@ export const fastDeepEqual = <T,>(a: T, b: T): boolean => {
67
67
  return isRegExp(b) && a.source === b.source && a.flags === b.flags;
68
68
  }
69
69
 
70
- if (a.valueOf !== Object.prototype.valueOf) {
71
- return a.valueOf() === b.valueOf();
70
+ // A null-prototype object (`Object.create(null)`, `Object.groupBy`) has
71
+ // neither method, so it falls through to the key comparison.
72
+ if (
73
+ typeof a.valueOf === 'function' &&
74
+ a.valueOf !== Object.prototype.valueOf
75
+ ) {
76
+ return typeof b.valueOf === 'function' && a.valueOf() === b.valueOf();
72
77
  }
73
78
 
74
- if (a.toString !== Object.prototype.toString) {
75
- return a.toString() === b.toString();
79
+ if (
80
+ typeof a.toString === 'function' &&
81
+ a.toString !== Object.prototype.toString
82
+ ) {
83
+ return typeof b.toString === 'function' && a.toString() === b.toString();
76
84
  }
77
85
 
78
86
  const aKeys: readonly string[] = Object.keys(a);
@@ -84,12 +92,11 @@ export const fastDeepEqual = <T,>(a: T, b: T): boolean => {
84
92
  aKeys.every(
85
93
  (key) =>
86
94
  hasKey(b, key) &&
87
- // React-specific: avoid traversing React elements' _owner.
88
- // _owner contains circular references
89
- // and is not needed when comparing the actual elements (and not their owners)
90
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
91
- !(key === '_owner' && a['$$typeof']) &&
92
- fastDeepEqual(a[key], b[key]),
95
+ // React-specific: a React element (marked by a symbol `$$typeof`)
96
+ // has its _owner skipped, i.e. treated as equal. It holds circular
97
+ // references and is not part of what the element renders.
98
+ ((key === '_owner' && typeof a['$$typeof'] === 'symbol') ||
99
+ fastDeepEqual(a[key], b[key])),
93
100
  )
94
101
  );
95
102
  }