ts-data-forge 6.13.1 → 7.0.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 (257) hide show
  1. package/README.md +2 -2
  2. package/dist/array/impl/array-utils-creation.d.mts.map +1 -1
  3. package/dist/array/impl/array-utils-creation.mjs +1 -1
  4. package/dist/array/impl/array-utils-creation.mjs.map +1 -1
  5. package/dist/array/impl/array-utils-iterators.mjs +7 -1
  6. package/dist/array/impl/array-utils-iterators.mjs.map +1 -1
  7. package/dist/array/impl/array-utils-modification.mjs +6 -0
  8. package/dist/array/impl/array-utils-modification.mjs.map +1 -1
  9. package/dist/array/impl/array-utils-reducing-value.d.mts +1 -1
  10. package/dist/array/impl/array-utils-reducing-value.d.mts.map +1 -1
  11. package/dist/array/impl/array-utils-reducing-value.mjs +6 -1
  12. package/dist/array/impl/array-utils-reducing-value.mjs.map +1 -1
  13. package/dist/array/impl/array-utils-search.d.mts +0 -2
  14. package/dist/array/impl/array-utils-search.d.mts.map +1 -1
  15. package/dist/array/impl/array-utils-search.mjs +6 -0
  16. package/dist/array/impl/array-utils-search.mjs.map +1 -1
  17. package/dist/array/impl/array-utils-set-op.d.mts.map +1 -1
  18. package/dist/array/impl/array-utils-set-op.mjs +0 -1
  19. package/dist/array/impl/array-utils-set-op.mjs.map +1 -1
  20. package/dist/array/impl/array-utils-slice-clamped.mjs +6 -0
  21. package/dist/array/impl/array-utils-slice-clamped.mjs.map +1 -1
  22. package/dist/array/impl/array-utils-slicing.mjs +6 -0
  23. package/dist/array/impl/array-utils-slicing.mjs.map +1 -1
  24. package/dist/array/impl/array-utils-transformation.d.mts +1 -1
  25. package/dist/array/impl/array-utils-transformation.d.mts.map +1 -1
  26. package/dist/array/impl/array-utils-transformation.mjs +8 -4
  27. package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
  28. package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
  29. package/dist/array/impl/array-utils-validation.mjs +9 -9
  30. package/dist/array/impl/array-utils-validation.mjs.map +1 -1
  31. package/dist/collections/imap-mapped.d.mts +622 -3
  32. package/dist/collections/imap-mapped.d.mts.map +1 -1
  33. package/dist/collections/imap-mapped.mjs +117 -18
  34. package/dist/collections/imap-mapped.mjs.map +1 -1
  35. package/dist/collections/imap.d.mts +3 -3
  36. package/dist/collections/imap.d.mts.map +1 -1
  37. package/dist/collections/imap.mjs +12 -5
  38. package/dist/collections/imap.mjs.map +1 -1
  39. package/dist/collections/iset-mapped.d.mts +4 -4
  40. package/dist/collections/iset-mapped.d.mts.map +1 -1
  41. package/dist/collections/iset-mapped.mjs +14 -4
  42. package/dist/collections/iset-mapped.mjs.map +1 -1
  43. package/dist/collections/iset.d.mts +2 -2
  44. package/dist/collections/iset.d.mts.map +1 -1
  45. package/dist/collections/iset.mjs +11 -2
  46. package/dist/collections/iset.mjs.map +1 -1
  47. package/dist/collections/queue.mjs +26 -20
  48. package/dist/collections/queue.mjs.map +1 -1
  49. package/dist/collections/stack.mjs +23 -17
  50. package/dist/collections/stack.mjs.map +1 -1
  51. package/dist/entry-point.mjs +6 -0
  52. package/dist/entry-point.mjs.map +1 -1
  53. package/dist/functional/optional/impl/optional-flat-map.d.mts +2 -2
  54. package/dist/functional/pipe.d.mts.map +1 -1
  55. package/dist/functional/pipe.mjs.map +1 -1
  56. package/dist/functional/result/impl/result-flat-map.d.mts +2 -2
  57. package/dist/functional/ternary-result/impl/ternary-result-flat-map.d.mts +5 -3
  58. package/dist/functional/ternary-result/impl/ternary-result-flat-map.d.mts.map +1 -1
  59. package/dist/functional/ternary-result/impl/ternary-result-flat-map.mjs.map +1 -1
  60. package/dist/guard/has-key.d.mts.map +1 -1
  61. package/dist/guard/has-key.mjs +1 -3
  62. package/dist/guard/has-key.mjs.map +1 -1
  63. package/dist/guard/is-non-null-object.d.mts.map +1 -1
  64. package/dist/guard/is-non-null-object.mjs +1 -3
  65. package/dist/guard/is-non-null-object.mjs.map +1 -1
  66. package/dist/guard/key-is-in.d.mts.map +1 -1
  67. package/dist/guard/key-is-in.mjs +1 -3
  68. package/dist/guard/key-is-in.mjs.map +1 -1
  69. package/dist/index.mjs +6 -0
  70. package/dist/index.mjs.map +1 -1
  71. package/dist/iterator/range.mjs +6 -0
  72. package/dist/iterator/range.mjs.map +1 -1
  73. package/dist/json/json.mjs +11 -7
  74. package/dist/json/json.mjs.map +1 -1
  75. package/dist/number/branded-types/finite-number.d.mts +1 -1
  76. package/dist/number/branded-types/index.d.mts +6 -0
  77. package/dist/number/branded-types/index.d.mts.map +1 -1
  78. package/dist/number/branded-types/index.mjs +6 -0
  79. package/dist/number/branded-types/index.mjs.map +1 -1
  80. package/dist/number/branded-types/int.d.mts +1 -2
  81. package/dist/number/branded-types/int.d.mts.map +1 -1
  82. package/dist/number/branded-types/int16.d.mts +2 -3
  83. package/dist/number/branded-types/int16.d.mts.map +1 -1
  84. package/dist/number/branded-types/int32.d.mts +2 -3
  85. package/dist/number/branded-types/int32.d.mts.map +1 -1
  86. package/dist/number/branded-types/negative-finite-number.d.mts +143 -0
  87. package/dist/number/branded-types/negative-finite-number.d.mts.map +1 -0
  88. package/dist/number/branded-types/negative-finite-number.mjs +156 -0
  89. package/dist/number/branded-types/negative-finite-number.mjs.map +1 -0
  90. package/dist/number/branded-types/negative-int.d.mts +120 -0
  91. package/dist/number/branded-types/negative-int.d.mts.map +1 -0
  92. package/dist/number/branded-types/negative-int.mjs +125 -0
  93. package/dist/number/branded-types/negative-int.mjs.map +1 -0
  94. package/dist/number/branded-types/negative-safe-int.d.mts +132 -0
  95. package/dist/number/branded-types/negative-safe-int.d.mts.map +1 -0
  96. package/dist/number/branded-types/negative-safe-int.mjs +136 -0
  97. package/dist/number/branded-types/negative-safe-int.mjs.map +1 -0
  98. package/dist/number/branded-types/non-negative-finite-number.d.mts +1 -1
  99. package/dist/number/branded-types/non-negative-int16.d.mts +1 -1
  100. package/dist/number/branded-types/non-negative-int32.d.mts +1 -1
  101. package/dist/number/branded-types/non-positive-finite-number.d.mts +130 -0
  102. package/dist/number/branded-types/non-positive-finite-number.d.mts.map +1 -0
  103. package/dist/number/branded-types/non-positive-finite-number.mjs +134 -0
  104. package/dist/number/branded-types/non-positive-finite-number.mjs.map +1 -0
  105. package/dist/number/branded-types/non-positive-int.d.mts +224 -0
  106. package/dist/number/branded-types/non-positive-int.d.mts.map +1 -0
  107. package/dist/number/branded-types/non-positive-int.mjs +222 -0
  108. package/dist/number/branded-types/non-positive-int.mjs.map +1 -0
  109. package/dist/number/branded-types/non-positive-safe-int.d.mts +119 -0
  110. package/dist/number/branded-types/non-positive-safe-int.d.mts.map +1 -0
  111. package/dist/number/branded-types/non-positive-safe-int.mjs +116 -0
  112. package/dist/number/branded-types/non-positive-safe-int.mjs.map +1 -0
  113. package/dist/number/branded-types/non-zero-finite-number.d.mts +5 -17
  114. package/dist/number/branded-types/non-zero-finite-number.d.mts.map +1 -1
  115. package/dist/number/branded-types/non-zero-finite-number.mjs +5 -17
  116. package/dist/number/branded-types/non-zero-finite-number.mjs.map +1 -1
  117. package/dist/number/branded-types/non-zero-int.d.mts +5 -33
  118. package/dist/number/branded-types/non-zero-int.d.mts.map +1 -1
  119. package/dist/number/branded-types/non-zero-int.mjs +5 -25
  120. package/dist/number/branded-types/non-zero-int.mjs.map +1 -1
  121. package/dist/number/branded-types/non-zero-int16.d.mts +6 -43
  122. package/dist/number/branded-types/non-zero-int16.d.mts.map +1 -1
  123. package/dist/number/branded-types/non-zero-int16.mjs +5 -25
  124. package/dist/number/branded-types/non-zero-int16.mjs.map +1 -1
  125. package/dist/number/branded-types/non-zero-int32.d.mts +6 -39
  126. package/dist/number/branded-types/non-zero-int32.d.mts.map +1 -1
  127. package/dist/number/branded-types/non-zero-int32.mjs +5 -25
  128. package/dist/number/branded-types/non-zero-int32.mjs.map +1 -1
  129. package/dist/number/branded-types/non-zero-safe-int.d.mts +5 -37
  130. package/dist/number/branded-types/non-zero-safe-int.d.mts.map +1 -1
  131. package/dist/number/branded-types/non-zero-safe-int.mjs +7 -30
  132. package/dist/number/branded-types/non-zero-safe-int.mjs.map +1 -1
  133. package/dist/number/branded-types/non-zero-uint16.d.mts +6 -33
  134. package/dist/number/branded-types/non-zero-uint16.d.mts.map +1 -1
  135. package/dist/number/branded-types/non-zero-uint16.mjs +5 -17
  136. package/dist/number/branded-types/non-zero-uint16.mjs.map +1 -1
  137. package/dist/number/branded-types/non-zero-uint32.d.mts +6 -31
  138. package/dist/number/branded-types/non-zero-uint32.d.mts.map +1 -1
  139. package/dist/number/branded-types/non-zero-uint32.mjs +5 -17
  140. package/dist/number/branded-types/non-zero-uint32.mjs.map +1 -1
  141. package/dist/number/branded-types/positive-finite-number.d.mts +4 -2
  142. package/dist/number/branded-types/positive-finite-number.d.mts.map +1 -1
  143. package/dist/number/branded-types/positive-int.d.mts +4 -2
  144. package/dist/number/branded-types/positive-int.d.mts.map +1 -1
  145. package/dist/number/branded-types/positive-int16.d.mts +4 -2
  146. package/dist/number/branded-types/positive-int16.d.mts.map +1 -1
  147. package/dist/number/branded-types/positive-int32.d.mts +4 -2
  148. package/dist/number/branded-types/positive-int32.d.mts.map +1 -1
  149. package/dist/number/branded-types/positive-safe-int.d.mts +4 -2
  150. package/dist/number/branded-types/positive-safe-int.d.mts.map +1 -1
  151. package/dist/number/branded-types/positive-safe-int.mjs +1 -1
  152. package/dist/number/branded-types/positive-uint16.d.mts +4 -2
  153. package/dist/number/branded-types/positive-uint16.d.mts.map +1 -1
  154. package/dist/number/branded-types/positive-uint32.d.mts +4 -2
  155. package/dist/number/branded-types/positive-uint32.d.mts.map +1 -1
  156. package/dist/number/branded-types/safe-int.d.mts +1 -2
  157. package/dist/number/branded-types/safe-int.d.mts.map +1 -1
  158. package/dist/number/branded-types/safe-int.mjs +2 -2
  159. package/dist/number/branded-types/safe-uint.d.mts +1 -1
  160. package/dist/number/branded-types/safe-uint.mjs +1 -1
  161. package/dist/number/branded-types/uint.d.mts +1 -1
  162. package/dist/number/branded-types/uint16.d.mts +1 -1
  163. package/dist/number/branded-types/uint32.d.mts +1 -1
  164. package/dist/number/index.d.mts +1 -0
  165. package/dist/number/index.d.mts.map +1 -1
  166. package/dist/number/index.mjs +6 -0
  167. package/dist/number/index.mjs.map +1 -1
  168. package/dist/number/num-arithmetic-types.d.mts +74 -0
  169. package/dist/number/num-arithmetic-types.d.mts.map +1 -0
  170. package/dist/number/num-arithmetic-types.mjs +2 -0
  171. package/dist/number/num-arithmetic-types.mjs.map +1 -0
  172. package/dist/number/num.d.mts +53 -9
  173. package/dist/number/num.d.mts.map +1 -1
  174. package/dist/number/num.mjs +67 -15
  175. package/dist/number/num.mjs.map +1 -1
  176. package/dist/number/refined-number-utils.d.mts +12 -9
  177. package/dist/number/refined-number-utils.d.mts.map +1 -1
  178. package/dist/number/refined-number-utils.mjs.map +1 -1
  179. package/dist/object/object.mjs +7 -1
  180. package/dist/object/object.mjs.map +1 -1
  181. package/dist/others/fast-deep-equal.d.mts.map +1 -1
  182. package/dist/others/fast-deep-equal.mjs +0 -1
  183. package/dist/others/fast-deep-equal.mjs.map +1 -1
  184. package/dist/others/unknown-to-string.mjs +2 -2
  185. package/dist/others/unknown-to-string.mjs.map +1 -1
  186. package/package.json +6 -6
  187. package/src/array/impl/array-utils-creation.mts +1 -1
  188. package/src/array/impl/array-utils-iterators.mts +1 -1
  189. package/src/array/impl/array-utils-reducing-value.mts +1 -2
  190. package/src/array/impl/array-utils-search.mts +0 -2
  191. package/src/array/impl/array-utils-set-op.mts +0 -1
  192. package/src/array/impl/array-utils-transformation.mts +8 -6
  193. package/src/array/impl/array-utils-validation.mts +1 -5
  194. package/src/collections/imap-mapped.mts +696 -34
  195. package/src/collections/imap-mapped.test.mts +61 -13
  196. package/src/collections/imap.mts +20 -9
  197. package/src/collections/imap.test.mts +9 -8
  198. package/src/collections/iset-mapped.mts +18 -10
  199. package/src/collections/iset-mapped.test.mts +6 -1
  200. package/src/collections/iset.mts +11 -4
  201. package/src/collections/iset.test.mts +6 -8
  202. package/src/collections/queue.mts +32 -32
  203. package/src/collections/stack.mts +26 -26
  204. package/src/functional/optional/impl/optional-flat-map.mts +2 -2
  205. package/src/functional/optional.test.mts +6 -4
  206. package/src/functional/pipe.mts +0 -1
  207. package/src/functional/pipe.test.mts +2 -1
  208. package/src/functional/result/impl/result-flat-map.mts +2 -2
  209. package/src/functional/result.test.mts +3 -2
  210. package/src/functional/ternary-result/impl/ternary-result-flat-map.mts +5 -3
  211. package/src/functional/ternary-result.test.mts +8 -3
  212. package/src/guard/has-key.mts +1 -3
  213. package/src/guard/is-non-null-object.mts +0 -1
  214. package/src/guard/key-is-in.mts +1 -3
  215. package/src/json/json.mts +5 -8
  216. package/src/json/json.test.mts +2 -2
  217. package/src/number/branded-types/index.mts +6 -0
  218. package/src/number/branded-types/negative-finite-number.mts +210 -0
  219. package/src/number/branded-types/negative-finite-number.test.mts +172 -0
  220. package/src/number/branded-types/negative-int.mts +160 -0
  221. package/src/number/branded-types/negative-int.test.mts +146 -0
  222. package/src/number/branded-types/negative-safe-int.mts +173 -0
  223. package/src/number/branded-types/negative-safe-int.test.mts +120 -0
  224. package/src/number/branded-types/non-positive-finite-number.mts +191 -0
  225. package/src/number/branded-types/non-positive-finite-number.test.mts +286 -0
  226. package/src/number/branded-types/non-positive-int.mts +251 -0
  227. package/src/number/branded-types/non-positive-int.test.mts +197 -0
  228. package/src/number/branded-types/non-positive-safe-int.mts +147 -0
  229. package/src/number/branded-types/non-positive-safe-int.test.mts +223 -0
  230. package/src/number/branded-types/non-zero-finite-number.mts +14 -22
  231. package/src/number/branded-types/non-zero-finite-number.test.mts +0 -12
  232. package/src/number/branded-types/non-zero-int.mts +10 -32
  233. package/src/number/branded-types/non-zero-int.test.mts +0 -20
  234. package/src/number/branded-types/non-zero-int16.mts +8 -32
  235. package/src/number/branded-types/non-zero-int16.test.mts +0 -30
  236. package/src/number/branded-types/non-zero-int32.mts +8 -32
  237. package/src/number/branded-types/non-zero-int32.test.mts +0 -30
  238. package/src/number/branded-types/non-zero-safe-int.mts +10 -37
  239. package/src/number/branded-types/non-zero-safe-int.test.mts +0 -32
  240. package/src/number/branded-types/non-zero-uint16.mts +8 -22
  241. package/src/number/branded-types/non-zero-uint16.test.mts +0 -16
  242. package/src/number/branded-types/non-zero-uint32.mts +8 -22
  243. package/src/number/branded-types/non-zero-uint32.test.mts +0 -16
  244. package/src/number/branded-types/positive-safe-int.mts +1 -1
  245. package/src/number/branded-types/safe-int.mts +2 -2
  246. package/src/number/branded-types/safe-uint.mts +1 -1
  247. package/src/number/index.mts +1 -0
  248. package/src/number/num-arithmetic-types.mts +265 -0
  249. package/src/number/num-arithmetic-types.test.mts +130 -0
  250. package/src/number/num-arithmetic.test.mts +102 -0
  251. package/src/number/num.mts +99 -18
  252. package/src/number/num.test.mts +2 -2
  253. package/src/number/refined-number-utils.mts +19 -9
  254. package/src/object/object.mts +2 -2
  255. package/src/others/fast-deep-equal.mts +0 -2
  256. package/src/others/map-nullable.test.mts +4 -3
  257. package/src/others/unknown-to-string.mts +2 -2
@@ -1,5 +1,6 @@
1
1
  import { type DeepReadonly } from 'ts-type-forge';
2
- import { Optional } from '../functional/index.mjs';
2
+ import { Optional, Result } from '../functional/index.mjs';
3
+ import { Num } from '../number/index.mjs';
3
4
  import { IMapMapped } from './imap-mapped.mjs';
4
5
 
5
6
  const toKey = (a: Readonly<{ v: number }>): number => a.v;
@@ -14,7 +15,10 @@ const testKeyToString = (key: Readonly<TestKey>): string =>
14
15
  const stringToTestKey = (str: string): TestKey => {
15
16
  const [type, idStr] = str.split('_');
16
17
 
17
- return { type: type ?? '', id: Number(idStr ?? '0') };
18
+ return {
19
+ type: type ?? '',
20
+ id: Result.unwrapOkOr(Num.safeParseFloat(idStr ?? '0'), Number.NaN),
21
+ };
18
22
  };
19
23
 
20
24
  describe('IMapMapped[Symbol.iterator]', () => {
@@ -112,7 +116,9 @@ describe('IMapMapped.create', () => {
112
116
  const [idStr, arrStr] = str.split('_');
113
117
 
114
118
  return {
115
- nested: { id: Number(idStr ?? '0') },
119
+ nested: {
120
+ id: Result.unwrapOkOr(Num.safeParseFloat(idStr ?? '0'), Number.NaN),
121
+ },
116
122
  arr: (arrStr ?? '').split(',').map(Number),
117
123
  };
118
124
  };
@@ -222,6 +228,31 @@ describe('IMapMapped.equal', () => {
222
228
 
223
229
  assert.isTrue(IMapMapped.equal(map1, map2));
224
230
  });
231
+
232
+ test('should return true for non-empty maps with identical entries', () => {
233
+ // Regression: the old implementation compared `b.get(k) === v` where
234
+ // `b.get(k)` returns `Optional<V>` and `v` is `V`, so the comparison was
235
+ // always false and `equal` never returned true for non-empty maps.
236
+ const map1 = IMapMapped.create(
237
+ [
238
+ [{ id: 1, type: 'user' }, 'Alice'],
239
+ [{ id: 2, type: 'admin' }, 'Bob'],
240
+ ],
241
+ testKeyToString,
242
+ stringToTestKey,
243
+ );
244
+
245
+ const map2 = IMapMapped.create(
246
+ [
247
+ [{ id: 2, type: 'admin' }, 'Bob'],
248
+ [{ id: 1, type: 'user' }, 'Alice'],
249
+ ],
250
+ testKeyToString,
251
+ stringToTestKey,
252
+ );
253
+
254
+ assert.isTrue(IMapMapped.equal(map1, map2));
255
+ });
225
256
  });
226
257
 
227
258
  describe('IMapMapped.size', () => {
@@ -398,6 +429,26 @@ describe('IMapMapped.set', () => {
398
429
  );
399
430
  });
400
431
 
432
+ test('should return same instance when setting an existing key with the same value', () => {
433
+ // Regression: the old implementation compared `value === this.get(key)`
434
+ // where `this.get(key)` returns `Optional<V>` and `value` is `V`, so the
435
+ // comparison was always false and the early return never fired — a new
436
+ // instance was always created even when the value was unchanged.
437
+ const s0 = IMapMapped.create(
438
+ [
439
+ [{ v: 1 }, '1'],
440
+ [{ v: 2 }, '2'],
441
+ [{ v: 3 }, '3'],
442
+ ],
443
+ toKey,
444
+ fromKey,
445
+ );
446
+
447
+ const result = s0.set({ v: 3 }, '3');
448
+
449
+ assert.isTrue(result === s0);
450
+ });
451
+
401
452
  test('case 3', () => {
402
453
  const s0 = IMapMapped.create<Readonly<{ v: number }>, string, number>(
403
454
  [],
@@ -602,7 +653,6 @@ describe('IMapMapped.some', () => {
602
653
  stringToTestKey,
603
654
  );
604
655
 
605
- // eslint-disable-next-line unicorn/prefer-includes
606
656
  assert.isTrue(map.some((value) => value === 'Alice'));
607
657
  });
608
658
 
@@ -616,7 +666,6 @@ describe('IMapMapped.some', () => {
616
666
  stringToTestKey,
617
667
  );
618
668
 
619
- // eslint-disable-next-line unicorn/prefer-includes
620
669
  assert.isFalse(map.some((value) => value === 'Charlie'));
621
670
  });
622
671
 
@@ -799,17 +848,16 @@ describe('IMapMapped.forEach', () => {
799
848
  stringToTestKey,
800
849
  );
801
850
 
802
- const mut_collected: [TestKey, string][] = [];
803
-
804
- for (const [key, value] of map.entries()) {
805
- mut_collected.push([key, value]);
806
- }
851
+ const collected: DeepReadonly<[TestKey, string][]> = Array.from(
852
+ map.entries(),
853
+ ([key, value]) => [key, value],
854
+ );
807
855
 
808
- expect(mut_collected).toHaveLength(2);
856
+ expect(collected).toHaveLength(2);
809
857
 
810
- expect(mut_collected).toContainEqual([{ id: 1, type: 'user' }, 'Alice']);
858
+ expect(collected).toContainEqual([{ id: 1, type: 'user' }, 'Alice']);
811
859
 
812
- expect(mut_collected).toContainEqual([{ id: 2, type: 'admin' }, 'Bob']);
860
+ expect(collected).toContainEqual([{ id: 2, type: 'admin' }, 'Bob']);
813
861
  });
814
862
  });
815
863
 
@@ -1,3 +1,4 @@
1
+ /* eslint-disable unicorn/prefer-iterator-to-array-at-end */
1
2
  import { type WidenLiteral } from 'ts-type-forge';
2
3
  import { Optional, pipe } from '../functional/index.mjs';
3
4
  import { asUint32 } from '../number/index.mjs';
@@ -87,7 +88,7 @@ type IMapInterface<K extends MapSetKeyType, V> = Readonly<{
87
88
  * ```
88
89
  *
89
90
  * @param key The key to retrieve.
90
- * @returns The value associated with the key wrapped with Optional.some, or
91
+ * @returns The value associated with the key wrapped with `Optional.some`, or
91
92
  * `Optional.none` if the key does not exist.
92
93
  */
93
94
  get: (key: K | (WidenLiteral<K> & {})) => Optional<V>;
@@ -390,9 +391,9 @@ type IMapInterface<K extends MapSetKeyType, V> = Readonly<{
390
391
  *
391
392
  * const mut_entries: (readonly [string, number])[] = [];
392
393
  *
393
- * for (const [key, value] of map.entries()) {
394
+ * map.forEach((value, key) => {
394
395
  * mut_entries.push([key, value]);
395
- * }
396
+ * });
396
397
  *
397
398
  * assert.deepStrictEqual(mut_entries, [
398
399
  * ['a', 1],
@@ -815,11 +816,11 @@ class IMapClass<K extends MapSetKeyType, V>
815
816
 
816
817
  if (!this.has(key)) {
817
818
  return IMap.create([...this.#map, tp(key, value)]);
818
- } else {
819
- return IMap.create(
820
- Array.from(this.#map, ([k, v]) => tp(k, Object.is(k, key) ? value : v)),
821
- );
822
819
  }
820
+
821
+ return IMap.create(
822
+ Array.from(this.#map, ([k, v]) => tp(k, Object.is(k, key) ? value : v)),
823
+ );
823
824
  }
824
825
 
825
826
  /** @inheritdoc */
@@ -853,7 +854,13 @@ class IMapClass<K extends MapSetKeyType, V>
853
854
  ): IMap<K, V> {
854
855
  const mut_result = new Map<K, V>(this.#map);
855
856
 
856
- for (const action of actions) {
857
+ const updateFn = (
858
+ action: Readonly<
859
+ | { type: 'delete'; key: K }
860
+ | { type: 'set'; key: K; value: V }
861
+ | { type: 'update'; key: K; updater: (value: V) => V }
862
+ >,
863
+ ): void => {
857
864
  switch (action.type) {
858
865
  case 'delete':
859
866
  mut_result.delete(action.key);
@@ -885,6 +892,10 @@ class IMapClass<K extends MapSetKeyType, V>
885
892
  break;
886
893
  }
887
894
  }
895
+ };
896
+
897
+ for (const action of actions) {
898
+ updateFn(action);
888
899
  }
889
900
 
890
901
  return IMap.create(mut_result);
@@ -909,7 +920,7 @@ class IMapClass<K extends MapSetKeyType, V>
909
920
 
910
921
  /** @inheritdoc */
911
922
  forEach(callbackfn: (value: V, key: K) => void): void {
912
- for (const [key, value] of this.#map.entries()) {
923
+ for (const [key, value] of this.#map) {
913
924
  callbackfn(value, key);
914
925
  }
915
926
  }
@@ -1,5 +1,6 @@
1
- import { Optional } from '../functional/index.mjs';
1
+ import { Optional, Result } from '../functional/index.mjs';
2
2
  import { isString } from '../guard/index.mjs';
3
+ import { Num } from '../number/index.mjs';
3
4
  import { IMap } from './imap.mjs';
4
5
 
5
6
  describe('IMap[Symbol.iterator]', () => {
@@ -829,7 +830,9 @@ describe('IMap.mapKeys', () => {
829
830
  ['2', 'two'],
830
831
  ]);
831
832
 
832
- const mapped = map.mapKeys((key) => Number.parseInt(key, 10));
833
+ const mapped = map.mapKeys((key) =>
834
+ Result.unwrapOkOr(Num.safeParseInt(key), Number.NaN),
835
+ );
833
836
 
834
837
  expect(Optional.unwrap(mapped.get(1))).toBe('one');
835
838
 
@@ -896,11 +899,10 @@ describe('IMap.forEach', () => {
896
899
  ['c', 3],
897
900
  ]);
898
901
 
899
- const mut_collected: [string, number][] = [];
900
-
901
- for (const [key, value] of map.entries()) {
902
- mut_collected.push([key, value]);
903
- }
902
+ const mut_collected: [string, number][] = Array.from(
903
+ map.entries(),
904
+ ([key, value]) => [key, value],
905
+ );
904
906
 
905
907
  expect(mut_collected).toHaveLength(3);
906
908
 
@@ -916,7 +918,6 @@ describe('IMap.forEach', () => {
916
918
 
917
919
  let mut_called = false;
918
920
 
919
- // eslint-disable-next-line unicorn/no-array-for-each
920
921
  map.forEach(() => {
921
922
  mut_called = true;
922
923
  });
@@ -1,4 +1,5 @@
1
- import { type ReadonlyRecord } from 'ts-type-forge';
1
+ /* eslint-disable unicorn/prefer-iterator-to-array-at-end */
2
+ import { type ReadonlyRecord, type WidenLiteral } from 'ts-type-forge';
2
3
  import { asUint32 } from '../number/index.mjs';
3
4
  import { type MapSetKeyType, type SizeType } from '../types.mjs';
4
5
 
@@ -126,7 +127,7 @@ type ISetMappedInterface<K, KM extends MapSetKeyType> = Readonly<{
126
127
  * @param key The element to check.
127
128
  * @returns `true` if the element exists, `false` otherwise.
128
129
  */
129
- has: (key: K) => boolean;
130
+ has: (key: K | (WidenLiteral<K> & {})) => boolean;
130
131
 
131
132
  // Reducing a value
132
133
 
@@ -471,9 +472,9 @@ type ISetMappedInterface<K, KM extends MapSetKeyType> = Readonly<{
471
472
  *
472
473
  * const mut_collected: Point[] = [];
473
474
  *
474
- * for (const point of set) {
475
+ * set.forEach((point) => {
475
476
  * mut_collected.push(point);
476
- * }
477
+ * });
477
478
  *
478
479
  * assert.deepStrictEqual(mut_collected, [
479
480
  * { x: 1, tag: 'a' },
@@ -1218,8 +1219,9 @@ class ISetMappedClass<K, KM extends MapSetKeyType>
1218
1219
  }
1219
1220
 
1220
1221
  /** @inheritdoc */
1221
- has(key: K): boolean {
1222
- return this.#set.has(this.#toKey(key));
1222
+ has(key: K | (WidenLiteral<K> & {})): boolean {
1223
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
1224
+ return this.#set.has(this.#toKey(key as K));
1223
1225
  }
1224
1226
 
1225
1227
  /** @inheritdoc */
@@ -1290,7 +1292,9 @@ class ISetMappedClass<K, KM extends MapSetKeyType>
1290
1292
  ): ISetMapped<K, KM> {
1291
1293
  const mut_result = new Set<KM>(this.#set);
1292
1294
 
1293
- for (const action of actions) {
1295
+ const updateFn = (
1296
+ action: Readonly<{ type: 'add'; key: K } | { type: 'delete'; key: K }>,
1297
+ ): void => {
1294
1298
  const key = this.#toKey(action.key);
1295
1299
 
1296
1300
  switch (action.type) {
@@ -1304,6 +1308,10 @@ class ISetMappedClass<K, KM extends MapSetKeyType>
1304
1308
 
1305
1309
  break;
1306
1310
  }
1311
+ };
1312
+
1313
+ for (const action of actions) {
1314
+ updateFn(action);
1307
1315
  }
1308
1316
 
1309
1317
  return ISetMapped.create<K, KM>(
@@ -1422,14 +1430,14 @@ class ISetMappedClass<K, KM extends MapSetKeyType>
1422
1430
 
1423
1431
  /** @inheritdoc */
1424
1432
  *keys(): IterableIterator<K> {
1425
- for (const km of this.#set.keys()) {
1433
+ for (const km of this.#set) {
1426
1434
  yield this.#fromKey(km);
1427
1435
  }
1428
1436
  }
1429
1437
 
1430
1438
  /** @inheritdoc */
1431
1439
  *values(): IterableIterator<K> {
1432
- for (const km of this.#set.keys()) {
1440
+ for (const km of this.#set) {
1433
1441
  // JavaScript Set's values() is an alias for keys()
1434
1442
  yield this.#fromKey(km);
1435
1443
  }
@@ -1437,7 +1445,7 @@ class ISetMappedClass<K, KM extends MapSetKeyType>
1437
1445
 
1438
1446
  /** @inheritdoc */
1439
1447
  *entries(): IterableIterator<readonly [K, K]> {
1440
- for (const km of this.#set.keys()) {
1448
+ for (const km of this.#set) {
1441
1449
  // JavaScript Set's entries() yields [value, value]
1442
1450
  const a = this.#fromKey(km);
1443
1451
 
@@ -1,3 +1,5 @@
1
+ import { Result } from '../functional/index.mjs';
2
+ import { Num } from '../number/index.mjs';
1
3
  import { ISetMapped } from './iset-mapped.mjs';
2
4
 
3
5
  const toKey = (a: Readonly<{ v: number }>): number => a.v;
@@ -13,7 +15,10 @@ const testElementToString = (el: Readonly<TestElement>): string =>
13
15
  const stringToTestElement = (str: string): TestElement => {
14
16
  const [type, idStr] = str.split('_');
15
17
 
16
- return { type: type ?? '', id: Number(idStr ?? '0') };
18
+ return {
19
+ type: type ?? '',
20
+ id: Result.unwrapOkOr(Num.safeParseFloat(idStr ?? '0'), Number.NaN),
21
+ };
17
22
  };
18
23
 
19
24
  describe('ISetMapped[Symbol.iterator]', () => {
@@ -1,3 +1,4 @@
1
+ /* eslint-disable unicorn/prefer-iterator-to-array-at-end */
1
2
  import { type ReadonlyRecord, type WidenLiteral } from 'ts-type-forge';
2
3
  import { asUint32 } from '../number/index.mjs';
3
4
  import { unknownToString } from '../others/index.mjs';
@@ -394,9 +395,9 @@ type ISetInterface<K extends MapSetKeyType> = Readonly<{
394
395
  *
395
396
  * const mut_collected: string[] = [];
396
397
  *
397
- * for (const value of set) {
398
+ * set.forEach((value) => {
398
399
  * mut_collected.push(value);
399
- * }
400
+ * });
400
401
  *
401
402
  * assert.deepStrictEqual(mut_collected, ['alpha', 'beta']);
402
403
  * ```
@@ -806,7 +807,9 @@ class ISetClass<K extends MapSetKeyType> implements ISet<K>, Iterable<K> {
806
807
  ): ISet<K> {
807
808
  const mut_result = new Set<K>(this.#set);
808
809
 
809
- for (const action of actions) {
810
+ const updateFn = (
811
+ action: Readonly<{ type: 'add'; key: K } | { type: 'delete'; key: K }>,
812
+ ): void => {
810
813
  switch (action.type) {
811
814
  case 'delete':
812
815
  mut_result.delete(action.key);
@@ -818,6 +821,10 @@ class ISetClass<K extends MapSetKeyType> implements ISet<K>, Iterable<K> {
818
821
 
819
822
  break;
820
823
  }
824
+ };
825
+
826
+ for (const action of actions) {
827
+ updateFn(action);
821
828
  }
822
829
 
823
830
  return ISet.create(mut_result);
@@ -846,7 +853,7 @@ class ISetClass<K extends MapSetKeyType> implements ISet<K>, Iterable<K> {
846
853
 
847
854
  /** @inheritdoc */
848
855
  forEach(callbackfn: (key: K) => void): void {
849
- for (const v of this.#set.values()) {
856
+ for (const v of this.#set) {
850
857
  callbackfn(v);
851
858
  }
852
859
  }
@@ -1,4 +1,4 @@
1
- import { type Primitive } from 'ts-type-forge';
1
+ import { type DeepReadonly, type Primitive } from 'ts-type-forge';
2
2
  import { expectType } from '../expect-type.mjs';
3
3
  import { isString } from '../guard/index.mjs';
4
4
  import { type MapSetKeyType } from '../types.mjs';
@@ -922,7 +922,6 @@ describe('ISet.forEach', () => {
922
922
 
923
923
  let mut_callCount = 0;
924
924
 
925
- // eslint-disable-next-line unicorn/no-array-for-each
926
925
  set.forEach(() => {
927
926
  mut_callCount += 1;
928
927
  });
@@ -978,14 +977,13 @@ describe('ISet.entries', () => {
978
977
  () => IterableIterator<readonly [1 | 2 | 3, 1 | 2 | 3]>
979
978
  >('<=');
980
979
 
981
- const mut_result: [1 | 2 | 3, 1 | 2 | 3][] = [];
982
-
983
- for (const x of s0.entries()) {
984
- mut_result.push([x[0], x[1]]);
985
- }
980
+ const result: DeepReadonly<[1 | 2 | 3, 1 | 2 | 3][]> = Array.from(
981
+ s0.entries(),
982
+ (x) => [x[0], x[1]],
983
+ );
986
984
 
987
985
  assert.deepStrictEqual(
988
- mut_result.toSorted((a, b) => a[0] - b[0]),
986
+ result.toSorted((a, b) => a[0] - b[0]),
989
987
  [
990
988
  [1, 1],
991
989
  [2, 2],
@@ -205,6 +205,9 @@ export type Queue<T> = Readonly<{
205
205
  * @implements Queue
206
206
  */
207
207
  class QueueClass<T> implements Queue<T> {
208
+ /** @internal Initial capacity for new queues. */
209
+ static readonly #INITIAL_CAPACITY = 8;
210
+
208
211
  /** @internal Circular buffer to store queue elements. */
209
212
  #mut_buffer: (T | undefined)[];
210
213
 
@@ -220,9 +223,6 @@ class QueueClass<T> implements Queue<T> {
220
223
  /** @internal Current capacity of the buffer. */
221
224
  #mut_capacity: number;
222
225
 
223
- /** @internal Initial capacity for new queues. */
224
- static readonly #INITIAL_CAPACITY = 8;
225
-
226
226
  /**
227
227
  * Constructs a new QueueClass instance.
228
228
  *
@@ -252,6 +252,35 @@ class QueueClass<T> implements Queue<T> {
252
252
  }
253
253
  }
254
254
 
255
+ /**
256
+ * @internal
257
+ * Resizes the circular buffer when it becomes full.
258
+ * Doubles the capacity and reorganizes elements to maintain queue order.
259
+ */
260
+ #resize(): void {
261
+ const newCapacity = asUint32(this.#mut_capacity * 2);
262
+
263
+ const newBuffer = Array.from<unknown, T | undefined>(
264
+ { length: newCapacity },
265
+ () => undefined,
266
+ );
267
+
268
+ // Copy elements in order from head to tail
269
+ for (const i of range(asSafeUint(this.#mut_size))) {
270
+ const sourceIndex = (this.#mut_head + i) % this.#mut_capacity;
271
+
272
+ newBuffer[i] = this.#mut_buffer[sourceIndex];
273
+ }
274
+
275
+ this.#mut_buffer = newBuffer;
276
+
277
+ this.#mut_head = 0;
278
+
279
+ this.#mut_tail = this.#mut_size;
280
+
281
+ this.#mut_capacity = newCapacity;
282
+ }
283
+
255
284
  /** @inheritdoc */
256
285
  get isEmpty(): boolean {
257
286
  return this.#mut_size === 0;
@@ -322,35 +351,6 @@ class QueueClass<T> implements Queue<T> {
322
351
 
323
352
  this.#mut_size += 1;
324
353
  }
325
-
326
- /**
327
- * @internal
328
- * Resizes the circular buffer when it becomes full.
329
- * Doubles the capacity and reorganizes elements to maintain queue order.
330
- */
331
- #resize(): void {
332
- const newCapacity = asUint32(this.#mut_capacity * 2);
333
-
334
- const newBuffer = Array.from<unknown, T | undefined>(
335
- { length: newCapacity },
336
- () => undefined,
337
- );
338
-
339
- // Copy elements in order from head to tail
340
- for (const i of range(asSafeUint(this.#mut_size))) {
341
- const sourceIndex = (this.#mut_head + i) % this.#mut_capacity;
342
-
343
- newBuffer[i] = this.#mut_buffer[sourceIndex];
344
- }
345
-
346
- this.#mut_buffer = newBuffer;
347
-
348
- this.#mut_head = 0;
349
-
350
- this.#mut_tail = this.#mut_size;
351
-
352
- this.#mut_capacity = newCapacity;
353
- }
354
354
  }
355
355
 
356
356
  /**
@@ -200,6 +200,9 @@ export type Stack<T> = Readonly<{
200
200
  * @implements Stack
201
201
  */
202
202
  class StackClass<T> implements Stack<T> {
203
+ /** @internal Initial capacity for new stacks. */
204
+ static readonly #INITIAL_CAPACITY = 8;
205
+
203
206
  /** @internal Dynamic array to store stack elements. */
204
207
  #mut_buffer: (T | undefined)[];
205
208
 
@@ -209,9 +212,6 @@ class StackClass<T> implements Stack<T> {
209
212
  /** @internal Current capacity of the buffer. */
210
213
  #mut_capacity: Uint32;
211
214
 
212
- /** @internal Initial capacity for new stacks. */
213
- static readonly #INITIAL_CAPACITY = 8;
214
-
215
215
  /**
216
216
  * Constructs a new StackClass instance.
217
217
  *
@@ -237,6 +237,29 @@ class StackClass<T> implements Stack<T> {
237
237
  }
238
238
  }
239
239
 
240
+ /**
241
+ * @internal
242
+ * Resizes the buffer when it becomes full.
243
+ * Doubles the capacity while preserving all elements.
244
+ */
245
+ #resize(): void {
246
+ const newCapacity = asUint32(this.#mut_capacity * 2);
247
+
248
+ const newBuffer = Array.from<unknown, T | undefined>(
249
+ { length: newCapacity },
250
+ () => undefined,
251
+ );
252
+
253
+ // Copy existing elements
254
+ for (const i of range(this.#mut_size)) {
255
+ newBuffer[i] = this.#mut_buffer[i];
256
+ }
257
+
258
+ this.#mut_buffer = newBuffer;
259
+
260
+ this.#mut_capacity = newCapacity;
261
+ }
262
+
240
263
  /** @inheritdoc */
241
264
  get isEmpty(): boolean {
242
265
  return this.#mut_size === 0;
@@ -303,29 +326,6 @@ class StackClass<T> implements Stack<T> {
303
326
 
304
327
  this.#mut_size = Uint32.add(this.#mut_size, 1);
305
328
  }
306
-
307
- /**
308
- * @internal
309
- * Resizes the buffer when it becomes full.
310
- * Doubles the capacity while preserving all elements.
311
- */
312
- #resize(): void {
313
- const newCapacity = asUint32(this.#mut_capacity * 2);
314
-
315
- const newBuffer = Array.from<unknown, T | undefined>(
316
- { length: newCapacity },
317
- () => undefined,
318
- );
319
-
320
- // Copy existing elements
321
- for (const i of range(this.#mut_size)) {
322
- newBuffer[i] = this.#mut_buffer[i];
323
- }
324
-
325
- this.#mut_buffer = newBuffer;
326
-
327
- this.#mut_capacity = newCapacity;
328
- }
329
329
  }
330
330
 
331
331
  /**
@@ -13,9 +13,9 @@ import { type Unwrap } from './types.mjs';
13
13
  *
14
14
  * ```ts
15
15
  * const parseNumber = (input: string): Optional<number> => {
16
- * const num = Number.parseInt(input, 10);
16
+ * const num = Num.safeParseInt(input);
17
17
  *
18
- * return Number.isNaN(num) ? Optional.none : Optional.some(num);
18
+ * return Result.isErr(num) ? Optional.none : Optional.some(num.value);
19
19
  * };
20
20
  *
21
21
  * const parsed = Optional.flatMap(Optional.some('10'), parseNumber);
@@ -1,7 +1,9 @@
1
1
  import { expectType } from '../expect-type.mjs';
2
+ import { Num } from '../number/index.mjs';
2
3
  import { type None, type Some } from '../types.mjs';
3
4
  import { Optional } from './optional/index.mjs';
4
5
  import { pipe } from './pipe.mjs';
6
+ import { Result } from './result/index.mjs';
5
7
 
6
8
  describe('Optional test', () => {
7
9
  describe('isOptional', () => {
@@ -374,7 +376,7 @@ describe('Optional test', () => {
374
376
  describe('flatMap', () => {
375
377
  test('should chain operations that return Optional', () => {
376
378
  const parseNumber = (s: string): Optional<number> => {
377
- const n = Number(s);
379
+ const n = Result.unwrapOkOr(Num.safeParseFloat(s), Number.NaN);
378
380
 
379
381
  return Number.isNaN(n) ? Optional.none : Optional.some(n);
380
382
  };
@@ -400,7 +402,7 @@ describe('Optional test', () => {
400
402
 
401
403
  test('should support chaining multiple flatMaps', () => {
402
404
  const parseNumber = (s: string): Optional<number> => {
403
- const n = Number(s);
405
+ const n = Result.unwrapOkOr(Num.safeParseFloat(s), Number.NaN);
404
406
 
405
407
  return Number.isNaN(n) ? Optional.none : Optional.some(n);
406
408
  };
@@ -424,7 +426,7 @@ describe('Optional test', () => {
424
426
 
425
427
  test('should support curried form', () => {
426
428
  const parseNumber = (s: string): Optional<number> => {
427
- const n = Number(s);
429
+ const n = Result.unwrapOkOr(Num.safeParseFloat(s), Number.NaN);
428
430
 
429
431
  return Number.isNaN(n) ? Optional.none : Optional.some(n);
430
432
  };
@@ -450,7 +452,7 @@ describe('Optional test', () => {
450
452
 
451
453
  test('should work with pipe when curried', () => {
452
454
  const parseNumber = (s: string): Optional<number> => {
453
- const n = Number(s);
455
+ const n = Result.unwrapOkOr(Num.safeParseFloat(s), Number.NaN);
454
456
 
455
457
  return Number.isNaN(n) ? Optional.none : Optional.some(n);
456
458
  };
@@ -49,7 +49,6 @@ export const pipe = <const A,>(a: A): Pipe<A> =>
49
49
  // the deferred true branch narrows `A` to `A & UnknownOptional`, so
50
50
  // `.value` would surface as the noisy `(A & UnknownOptional) | A`. Keeping
51
51
  // it on the unconditional base yields a clean `A`.
52
- // transformer-ignore-next-line convert-to-readonly
53
52
  export type Pipe<A> = PipeBase<A> &
54
53
  PipeMapNullable<A> &
55
54
  ([A] extends [UnknownOptional] ? PipeMapOptional<A> : unknown);
@@ -1,4 +1,5 @@
1
1
  import { expectType } from '../expect-type.mjs';
2
+ import { Num } from '../number/index.mjs';
2
3
  import { type None, type Some } from '../types.mjs';
3
4
  import { Optional } from './optional/index.mjs';
4
5
  import { pipe } from './pipe.mjs';
@@ -89,7 +90,7 @@ describe(pipe, () => {
89
90
 
90
91
  test('chaining multiple operations', () => {
91
92
  const result = pipe('5')
92
- .map((s) => Number.parseInt(s, 10))
93
+ .map((s) => Result.unwrapOkOr(Num.safeParseInt(s), Number.NaN))
93
94
  .map((n) => n * 3)
94
95
  .map((n) => n.toString())
95
96
  .map((s) => `${s} items`).value;