ts-data-forge 14.0.1 → 14.2.0

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 (94) hide show
  1. package/README.md +5 -5
  2. package/dist/array/impl/array-utils-transformation.d.mts +4 -1
  3. package/dist/array/impl/array-utils-transformation.d.mts.map +1 -1
  4. package/dist/array/impl/array-utils-transformation.mjs +4 -5
  5. package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
  6. package/dist/array/impl/array-utils-validation.d.mts +8 -3
  7. package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
  8. package/dist/array/impl/array-utils-validation.mjs.map +1 -1
  9. package/dist/collections/imap-mapped.mjs +1 -1
  10. package/dist/collections/imap-mapped.mjs.map +1 -1
  11. package/dist/collections/imap.mjs +1 -1
  12. package/dist/collections/imap.mjs.map +1 -1
  13. package/dist/functional/match.d.mts.map +1 -1
  14. package/dist/functional/match.mjs.map +1 -1
  15. package/dist/functional/optional/impl/optional-map.d.mts +1 -1
  16. package/dist/functional/optional/impl/optional-map.d.mts.map +1 -1
  17. package/dist/functional/optional/impl/optional-map.mjs.map +1 -1
  18. package/dist/functional/result/impl/result-map-err.d.mts +1 -1
  19. package/dist/functional/result/impl/result-map-err.d.mts.map +1 -1
  20. package/dist/functional/result/impl/result-map-err.mjs.map +1 -1
  21. package/dist/functional/result/impl/result-map.d.mts +1 -1
  22. package/dist/functional/result/impl/result-map.d.mts.map +1 -1
  23. package/dist/functional/result/impl/result-map.mjs.map +1 -1
  24. package/dist/number/branded-types/int.d.mts +1 -1
  25. package/dist/number/branded-types/int16.d.mts +1 -1
  26. package/dist/number/branded-types/int32.d.mts +1 -1
  27. package/dist/number/branded-types/non-negative-int16.d.mts +1 -1
  28. package/dist/number/branded-types/non-negative-int32.d.mts +1 -1
  29. package/dist/number/branded-types/non-positive-finite-number.mjs +1 -1
  30. package/dist/number/branded-types/non-positive-finite-number.mjs.map +1 -1
  31. package/dist/number/branded-types/positive-int.d.mts +1 -1
  32. package/dist/number/branded-types/positive-int16.d.mts +1 -1
  33. package/dist/number/branded-types/positive-int32.d.mts +1 -1
  34. package/dist/number/branded-types/positive-safe-int.d.mts +1 -1
  35. package/dist/number/branded-types/positive-uint16.d.mts +1 -1
  36. package/dist/number/branded-types/positive-uint32.d.mts +1 -1
  37. package/dist/number/branded-types/safe-int.d.mts +1 -1
  38. package/dist/number/branded-types/safe-uint.d.mts +1 -1
  39. package/dist/number/branded-types/uint.d.mts +1 -1
  40. package/dist/number/branded-types/uint16.d.mts +1 -1
  41. package/dist/number/branded-types/uint32.d.mts +1 -1
  42. package/dist/number/enum/int8.d.mts +2 -2
  43. package/dist/number/enum/int8.d.mts.map +1 -1
  44. package/dist/number/enum/int8.mjs.map +1 -1
  45. package/dist/number/enum/uint8.d.mts +2 -2
  46. package/dist/number/enum/uint8.d.mts.map +1 -1
  47. package/dist/number/enum/uint8.mjs.map +1 -1
  48. package/dist/number/num-arithmetic-types.d.mts +8 -9
  49. package/dist/number/num-arithmetic-types.d.mts.map +1 -1
  50. package/dist/number/refined-number-utils.d.mts +2 -2
  51. package/dist/number/refined-number-utils.d.mts.map +1 -1
  52. package/dist/number/refined-number-utils.mjs.map +1 -1
  53. package/dist/object/object.d.mts +12 -12
  54. package/dist/object/object.d.mts.map +1 -1
  55. package/dist/object/object.mjs +5 -1
  56. package/dist/object/object.mjs.map +1 -1
  57. package/dist/others/fast-deep-equal.d.mts.map +1 -1
  58. package/dist/others/fast-deep-equal.mjs +2 -2
  59. package/dist/others/fast-deep-equal.mjs.map +1 -1
  60. package/package.json +2 -2
  61. package/src/array/impl/array-utils-modification.test.mts +26 -5
  62. package/src/array/impl/array-utils-shape-invariants.test.mts +259 -17
  63. package/src/array/impl/array-utils-slicing.test.mts +11 -3
  64. package/src/array/impl/array-utils-transformation.mts +41 -18
  65. package/src/array/impl/array-utils-transformation.test.mts +10 -3
  66. package/src/array/impl/array-utils-validation.mts +54 -10
  67. package/src/array/impl/array-utils-validation.test.mts +153 -3
  68. package/src/collections/imap-mapped.mts +1 -1
  69. package/src/collections/imap-mapped.test.mts +2 -2
  70. package/src/collections/imap.mts +1 -1
  71. package/src/collections/iset-mapped.test.mts +1 -1
  72. package/src/functional/match.mts +5 -4
  73. package/src/functional/optional/impl/optional-map.mts +16 -1
  74. package/src/functional/optional.test.mts +48 -1
  75. package/src/functional/result/impl/result-map-err.mts +16 -1
  76. package/src/functional/result/impl/result-map.mts +16 -1
  77. package/src/functional/result.test.mts +83 -3
  78. package/src/guard/is-primitive.test.mts +1 -1
  79. package/src/guard/is-record.test.mts +4 -4
  80. package/src/guard/is-type.test.mts +2 -2
  81. package/src/number/branded-types/finite-number.test.mts +2 -2
  82. package/src/number/branded-types/non-negative-finite-number.test.mts +2 -2
  83. package/src/number/branded-types/non-positive-finite-number.mts +1 -1
  84. package/src/number/branded-types/non-positive-finite-number.test.mts +5 -9
  85. package/src/number/branded-types/non-zero-finite-number.test.mts +2 -2
  86. package/src/number/branded-types/positive-finite-number.test.mts +2 -2
  87. package/src/number/enum/int8.mts +2 -1
  88. package/src/number/enum/uint8.mts +6 -2
  89. package/src/number/num-arithmetic-types.mts +125 -24
  90. package/src/number/num.test.mts +9 -9
  91. package/src/number/refined-number-utils.mts +2 -1
  92. package/src/object/object.mts +29 -17
  93. package/src/object/object.test.mts +2 -1
  94. package/src/others/fast-deep-equal.mts +6 -4
@@ -11,7 +11,7 @@ describe('PositiveFiniteNumber test', () => {
11
11
  test('accepts valid positive finite numbers', () => {
12
12
  expect(() => asPositiveFiniteNumber(1)).not.toThrow();
13
13
 
14
- expect(() => asPositiveFiniteNumber(3.14)).not.toThrow();
14
+ expect(() => asPositiveFiniteNumber(1.23)).not.toThrow();
15
15
 
16
16
  expect(() => asPositiveFiniteNumber(0.5)).not.toThrow();
17
17
 
@@ -75,7 +75,7 @@ describe('PositiveFiniteNumber test', () => {
75
75
  test('correctly identifies positive finite numbers', () => {
76
76
  assert.isTrue(isPositiveFiniteNumber(1));
77
77
 
78
- assert.isTrue(isPositiveFiniteNumber(3.14));
78
+ assert.isTrue(isPositiveFiniteNumber(1.23));
79
79
 
80
80
  assert.isTrue(isPositiveFiniteNumber(0.5));
81
81
 
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  type AbsoluteValue,
3
3
  type Int16,
4
+ type StrictExclude,
4
5
  type Int8 as TtfImported_Int8,
5
6
  } from 'ts-type-forge';
6
7
  import { expectType } from '../../expect-type.mjs';
@@ -54,7 +55,7 @@ const sub = (x: Int8, y: Int8): Int8 => fromNumber(x - y);
54
55
 
55
56
  const mul = (x: Int8, y: Int8): Int8 => fromNumber(x * y);
56
57
 
57
- const div = (x: Int8, y: Exclude<Int8, 0>): Int8 =>
58
+ const div = (x: Int8, y: StrictExclude<Int8, 0>): Int8 =>
58
59
  fromNumber(Math.floor(x / y));
59
60
 
60
61
  const random = (min: Int8, max: Int8): Int8 =>
@@ -1,4 +1,8 @@
1
- import { type Uint8 as TtfImported_Uint8, type Uint16 } from 'ts-type-forge';
1
+ import {
2
+ type StrictExclude,
3
+ type Uint8 as TtfImported_Uint8,
4
+ type Uint16,
5
+ } from 'ts-type-forge';
2
6
  import { expectType } from '../../expect-type.mjs';
3
7
  import { TsDataForgeInternals } from '../refined-number-utils.mjs';
4
8
 
@@ -44,7 +48,7 @@ const sub = (x: Uint8, y: Uint8): Uint8 => fromNumber(x - y);
44
48
 
45
49
  const mul = (x: Uint8, y: Uint8): Uint8 => fromNumber(x * y);
46
50
 
47
- const div = (x: Uint8, y: Exclude<Uint8, 0>): Uint8 =>
51
+ const div = (x: Uint8, y: StrictExclude<Uint8, 0>): Uint8 =>
48
52
  fromNumber(Math.floor(x / y));
49
53
 
50
54
  const random = (min: Uint8, max: Uint8): Uint8 =>
@@ -1,4 +1,6 @@
1
1
  import {
2
+ type BoolAnd,
3
+ type BoolOr,
2
4
  type FiniteNumber,
3
5
  type Int,
4
6
  type NegativeFiniteNumber,
@@ -6,6 +8,7 @@ import {
6
8
  type NegativeNumber,
7
9
  type NegativeSafeInt,
8
10
  type NonNegativeFiniteNumber,
11
+ type NonNegativeInt,
9
12
  type NonNegativeNumber,
10
13
  type NonPositiveFiniteNumber,
11
14
  type NonPositiveInt,
@@ -23,6 +26,7 @@ import {
23
26
  type SafeUint,
24
27
  type Uint,
25
28
  } from 'ts-type-forge';
29
+ import { expectType } from '../expect-type.mjs';
26
30
 
27
31
  /**
28
32
  * Internal type-level arithmetic for {@link Num}'s `add`/`sub`/`mul`/`div`.
@@ -75,16 +79,6 @@ export type LevelOf<N> = [N] extends [SafeInt]
75
79
 
76
80
  // --- boolean fact algebra over a sign ----------------------------------------
77
81
 
78
- type And<A extends boolean, B extends boolean> = [A] extends [true]
79
- ? [B] extends [true]
80
- ? true
81
- : false
82
- : false;
83
-
84
- type Or<A extends boolean, B extends boolean> = true extends A | B
85
- ? true
86
- : false;
87
-
88
82
  /** Whether the value is guaranteed `>= 0`. */
89
83
  type Ge0<S extends Sign> = S extends 'pos' | 'nonneg' ? true : false;
90
84
 
@@ -95,7 +89,7 @@ type Le0<S extends Sign> = S extends 'neg' | 'nonpos' ? true : false;
95
89
  type Ne0<S extends Sign> = S extends 'pos' | 'neg' | 'nonzero' ? true : false;
96
90
 
97
91
  /** Rebuilds a {@link Sign} from the three facts. */
98
- type FromFacts<
92
+ type SignFromFacts<
99
93
  ge0 extends boolean,
100
94
  le0 extends boolean,
101
95
  ne0 extends boolean,
@@ -111,16 +105,16 @@ type FromFacts<
111
105
  ? 'nonzero'
112
106
  : 'any';
113
107
 
114
- type NegateSign<S extends Sign> = FromFacts<Le0<S>, Ge0<S>, Ne0<S>>;
108
+ type NegateSign<S extends Sign> = SignFromFacts<Le0<S>, Ge0<S>, Ne0<S>>;
115
109
 
116
110
  // --- sign combination per operation ------------------------------------------
117
111
 
118
- type AddSign<A extends Sign, B extends Sign> = FromFacts<
119
- And<Ge0<A>, Ge0<B>>,
120
- And<Le0<A>, Le0<B>>,
121
- Or<
122
- And<And<Ge0<A>, Ge0<B>>, Or<Ne0<A>, Ne0<B>>>,
123
- And<And<Le0<A>, Le0<B>>, Or<Ne0<A>, Ne0<B>>>
112
+ type AddSign<A extends Sign, B extends Sign> = SignFromFacts<
113
+ BoolAnd<Ge0<A>, Ge0<B>>,
114
+ BoolAnd<Le0<A>, Le0<B>>,
115
+ BoolOr<
116
+ BoolAnd<BoolAnd<Ge0<A>, Ge0<B>>, BoolOr<Ne0<A>, Ne0<B>>>,
117
+ BoolAnd<BoolAnd<Le0<A>, Le0<B>>, BoolOr<Ne0<A>, Ne0<B>>>
124
118
  >
125
119
  >;
126
120
 
@@ -131,16 +125,16 @@ type MulSignWithNe0<
131
125
  A extends Sign,
132
126
  B extends Sign,
133
127
  ne0 extends boolean,
134
- > = FromFacts<
135
- Or<And<Ge0<A>, Ge0<B>>, And<Le0<A>, Le0<B>>>,
136
- Or<And<Ge0<A>, Le0<B>>, And<Le0<A>, Ge0<B>>>,
128
+ > = SignFromFacts<
129
+ BoolOr<BoolAnd<Ge0<A>, Ge0<B>>, BoolAnd<Le0<A>, Le0<B>>>,
130
+ BoolOr<BoolAnd<Ge0<A>, Le0<B>>, BoolAnd<Le0<A>, Ge0<B>>>,
137
131
  ne0
138
132
  >;
139
133
 
140
134
  type MulSign<A extends Sign, B extends Sign> = MulSignWithNe0<
141
135
  A,
142
136
  B,
143
- And<Ne0<A>, Ne0<B>>
137
+ BoolAnd<Ne0<A>, Ne0<B>>
144
138
  >;
145
139
 
146
140
  /** Exact division: nonzero dividend over nonzero divisor stays nonzero. */
@@ -160,9 +154,12 @@ type LooserLevel<LA extends Level, LB extends Level> = 'number' extends LA | LB
160
154
  ? 'int'
161
155
  : 'safeint';
162
156
 
157
+ type WidenSafeIntToInt<T> = T extends 'safeint' ? 'int' : T;
158
+
163
159
  /** `+`/`-`/`*`: looser level, widening `safeint -> int` (overflow may occur). */
164
- type AddMulLevel<LA extends Level, LB extends Level> =
165
- LooserLevel<LA, LB> extends 'safeint' ? 'int' : LooserLevel<LA, LB>;
160
+ type AddMulLevel<LA extends Level, LB extends Level> = WidenSafeIntToInt<
161
+ LooserLevel<LA, LB>
162
+ >;
166
163
 
167
164
  /** Exact division yields a (possibly fractional) finite number. */
168
165
  type DivLevel<LA extends Level, LB extends Level> = 'number' extends LA | LB
@@ -267,3 +264,107 @@ export type DivIntResult<A, B> = Reconstruct<
267
264
  DivIntSign<SignOf<A>, SignOf<B>>,
268
265
  DivIntLevel<LevelOf<A>, LevelOf<B>>
269
266
  >;
267
+
268
+ // #region type tests
269
+ {
270
+ type A = NonPositiveInt;
271
+
272
+ type B = NonPositiveInt;
273
+
274
+ expectType<MulResult<A, B>, Uint>('=');
275
+
276
+ type SignA = SignOf<A>;
277
+
278
+ type SignB = SignOf<B>;
279
+
280
+ expectType<SignA, 'nonpos'>('=');
281
+
282
+ expectType<MulSign<SignA, SignB>, 'nonneg'>('=');
283
+
284
+ expectType<BoolAnd<Ne0<SignA>, Ne0<SignB>>, false>('=');
285
+
286
+ expectType<MulSignWithNe0<SignA, SignB, false>, 'nonneg'>('=');
287
+
288
+ expectType<
289
+ BoolOr<BoolAnd<Ge0<SignA>, Ge0<SignB>>, BoolAnd<Le0<SignA>, Le0<SignB>>>,
290
+ true
291
+ >('=');
292
+
293
+ expectType<Ge0<SignA>, false>('=');
294
+
295
+ expectType<Ge0<SignB>, false>('=');
296
+
297
+ expectType<Le0<SignA>, true>('=');
298
+
299
+ expectType<Le0<SignB>, true>('=');
300
+
301
+ expectType<BoolAnd<Ge0<SignA>, Ge0<SignB>>, false>('=');
302
+
303
+ expectType<BoolAnd<true, true>, true>('=');
304
+
305
+ expectType<
306
+ BoolOr<BoolAnd<Ge0<SignA>, Le0<SignB>>, BoolAnd<Le0<SignA>, Ge0<SignB>>>,
307
+ false
308
+ >('=');
309
+
310
+ expectType<SignFromFacts<true, false, false>, 'nonneg'>('=');
311
+
312
+ expectType<AddMulLevel<LevelOf<A>, LevelOf<B>>, 'int'>('=');
313
+
314
+ type LA = LevelOf<A>;
315
+
316
+ type LB = LevelOf<B>;
317
+
318
+ expectType<LA, 'int'>('=');
319
+
320
+ expectType<LooserLevel<LA, LB>, 'int'>('=');
321
+
322
+ {
323
+ type MulResult2<X, Y> = Reconstruct<
324
+ MulSign<SignOf<X>, SignOf<Y>>,
325
+ AddMulLevel<LevelOf<X>, LevelOf<Y>>
326
+ >;
327
+
328
+ expectType<MulResult<A, B>, MulResult2<A, B>>('=');
329
+
330
+ expectType<
331
+ MulResult<A, B>,
332
+ Reconstruct<
333
+ MulSign<SignOf<A>, SignOf<B>>,
334
+ AddMulLevel<LevelOf<A>, LevelOf<B>>
335
+ >
336
+ >('=');
337
+
338
+ expectType<
339
+ Reconstruct<
340
+ MulSign<SignOf<A>, SignOf<B>>,
341
+ AddMulLevel<LevelOf<A>, LevelOf<B>>
342
+ >,
343
+ NonNegativeInt
344
+ >('=');
345
+
346
+ expectType<
347
+ Reconstruct<MulSign<SignA, SignB>, AddMulLevel<LA, LB>>,
348
+ NonNegativeInt
349
+ >('=');
350
+
351
+ expectType<Reconstruct<'nonneg', 'int'>, NonNegativeInt>('=');
352
+
353
+ expectType<MulResult<A, B>, NonNegativeInt>('=');
354
+
355
+ // type Reconstruct<S extends Sign, L extends Level> = L extends 'safeint'
356
+ // ? ReconstructSafeInt<S>
357
+ // : L extends 'int'
358
+ // ? ReconstructInt<S>
359
+ // : L extends 'finite'
360
+ // ? ReconstructFinite<S>
361
+ // : ReconstructNumber<S>;
362
+ }
363
+
364
+ // FromFacts<
365
+ // BoolOr<BoolAnd<Ge0<A>, Ge0<B>>, BoolAnd<Le0<A>, Le0<B>>>,
366
+ // BoolOr<BoolAnd<Ge0<A>, Le0<B>>, BoolAnd<Le0<A>, Ge0<B>>>,
367
+ // ne0
368
+ // >;
369
+ }
370
+ // #endregion
@@ -244,8 +244,8 @@ describe('Num test', () => {
244
244
  });
245
245
 
246
246
  test('composes with Result.unwrapOk + nullish fallback', () => {
247
- expect(Result.unwrapOk(Num.safeParseFloat('3.14')) ?? Number.NaN).toBe(
248
- 3.14,
247
+ expect(Result.unwrapOk(Num.safeParseFloat('1.23')) ?? Number.NaN).toBe(
248
+ 1.23,
249
249
  );
250
250
 
251
251
  expect(Result.unwrapOk(Num.safeParseFloat('')) ?? Number.NaN).toBeNaN();
@@ -360,9 +360,9 @@ describe('Num test', () => {
360
360
 
361
361
  describe('roundAt', () => {
362
362
  test('rounds to specified decimal places', () => {
363
- expect(Num.roundAt(3.141_59, 2)).toBe(3.14);
363
+ expect(Num.roundAt(1.234_56, 2)).toBe(1.23);
364
364
 
365
- expect(Num.roundAt(3.141_59, 3)).toBe(3.142);
365
+ expect(Num.roundAt(1.234_56, 3)).toBe(1.235);
366
366
 
367
367
  expect(Num.roundAt(2.555, 2)).toBe(2.56);
368
368
 
@@ -388,21 +388,21 @@ describe('Num test', () => {
388
388
  test('creates rounding function with specified precision', () => {
389
389
  const round2 = Num.round(2);
390
390
 
391
- expect(round2(3.141_59)).toBe(3.14);
391
+ expect(round2(1.234_56)).toBe(1.23);
392
392
 
393
393
  expect(round2(2.556)).toBe(2.56);
394
394
 
395
395
  const round1 = Num.round(1);
396
396
 
397
- expect(round1(3.141_59)).toBe(3.1);
397
+ expect(round1(1.234_56)).toBe(1.2);
398
398
 
399
399
  expect(round1(2.56)).toBe(2.6);
400
400
 
401
401
  const round3 = Num.round(3);
402
402
 
403
- expect(round3(3.1416)).toBe(3.142);
403
+ expect(round3(1.2346)).toBe(1.235);
404
404
 
405
- expect(round3(2.7182)).toBe(2.718);
405
+ expect(round3(4.5678)).toBe(4.568);
406
406
  });
407
407
  });
408
408
 
@@ -412,7 +412,7 @@ describe('Num test', () => {
412
412
 
413
413
  expect(Num.mapNaN2Undefined(0)).toBe(0);
414
414
 
415
- expect(Num.mapNaN2Undefined(-3.14)).toBe(-3.14);
415
+ expect(Num.mapNaN2Undefined(-1.23)).toBe(-1.23);
416
416
 
417
417
  expect(Num.mapNaN2Undefined(Number.NaN)).toBeUndefined();
418
418
  });
@@ -8,6 +8,7 @@ import {
8
8
  type NonZeroNumber,
9
9
  type PositiveNumber,
10
10
  type RelaxedExclude,
11
+ type StrictPick,
11
12
  type TypeEq,
12
13
  type UnknownBrand,
13
14
  type UnwrapBrandFalseKeys,
@@ -167,7 +168,7 @@ export namespace TsDataForgeInternals {
167
168
  fromNumber: (a: number) => N;
168
169
  }>
169
170
  : unknown) &
170
- Pick<
171
+ StrictPick<
171
172
  Readonly<{
172
173
  add: (x: N, y: N) => N;
173
174
  sub: (x: N, y: N) => N;
@@ -7,7 +7,11 @@ import {
7
7
  type IsUnion,
8
8
  type List,
9
9
  type ReadonlyRecord,
10
+ type RelaxedExclude,
11
+ type RelaxedOmit,
10
12
  type RelaxedPick,
13
+ type StrictOmit,
14
+ type StrictPick,
11
15
  type TypeEq,
12
16
  type UnknownRecord,
13
17
  } from 'ts-type-forge';
@@ -125,7 +129,7 @@ export namespace Obj {
125
129
  export function pick<
126
130
  const R extends UnknownRecord,
127
131
  const Keys extends readonly (keyof R)[],
128
- >(record: R, keys: Keys): Pick<R, ArrayElement<Keys>>;
132
+ >(record: R, keys: Keys): StrictPick<R, ArrayElement<Keys>>;
129
133
 
130
134
  // Curried version
131
135
  export function pick<const Keys extends readonly PropertyKey[]>(
@@ -140,7 +144,7 @@ export namespace Obj {
140
144
  >(
141
145
  ...args: readonly [record: R, keys: Keys] | readonly [keys: Keys]
142
146
  ):
143
- | Pick<R, ArrayElement<Keys>>
147
+ | StrictPick<R, ArrayElement<Keys>>
144
148
  | ((record: R) => RelaxedPick<R, ArrayElement<Keys>>) {
145
149
  switch (args.length) {
146
150
  case 2: {
@@ -212,12 +216,14 @@ export namespace Obj {
212
216
  export function omit<
213
217
  const R extends UnknownRecord,
214
218
  const Keys extends readonly (keyof R)[],
215
- >(record: R, keys: Keys): Omit<R, ArrayElement<Keys>>;
219
+ >(record: R, keys: Keys): StrictOmit<R, ArrayElement<Keys>>;
216
220
 
217
221
  // Curried version
218
222
  export function omit<const Keys extends readonly PropertyKey[]>(
219
223
  keys: Keys,
220
- ): <const R extends UnknownRecord>(record: R) => Omit<R, ArrayElement<Keys>>;
224
+ ): <const R extends UnknownRecord>(
225
+ record: R,
226
+ ) => RelaxedOmit<R, ArrayElement<Keys>>;
221
227
 
222
228
  export function omit<
223
229
  const R extends UnknownRecord,
@@ -226,7 +232,8 @@ export namespace Obj {
226
232
  ...args:
227
233
  readonly [record: R, keys: Keys] | readonly [keys: readonly PropertyKey[]]
228
234
  ):
229
- Omit<R, ArrayElement<Keys>> | ((record: R) => Omit<R, ArrayElement<Keys>>) {
235
+ | StrictOmit<R, ArrayElement<Keys>>
236
+ | ((record: R) => StrictOmit<R, ArrayElement<Keys>>) {
230
237
  switch (args.length) {
231
238
  case 2: {
232
239
  const [record, keys] = args;
@@ -246,10 +253,11 @@ export namespace Obj {
246
253
 
247
254
  return <R2 extends UnknownRecord>(record: R2) => {
248
255
  // eslint-disable-next-line total-functions/no-unsafe-type-assertion
249
- const result = omit(record, keys as readonly (keyof R2)[]) as Omit<
250
- R2,
251
- ArrayElement<Keys>
252
- >;
256
+ const result = omit(
257
+ record,
258
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
259
+ keys as readonly (keyof R2)[],
260
+ ) as RelaxedOmit<R2, ArrayElement<Keys>>;
253
261
 
254
262
  return result;
255
263
  };
@@ -544,7 +552,7 @@ declare namespace TsDataForgeInternals {
544
552
  TypeEq<Entries['length'], 0> extends true
545
553
  ? R
546
554
  : EntriesToObjectImpl<
547
- R & Readonly<Record<Entries[0][0], Entries[0][1]>>,
555
+ R & ReadonlyRecord<Entries[0][0], Entries[0][1]>,
548
556
  List.Tail<Entries>
549
557
  >;
550
558
 
@@ -639,12 +647,12 @@ declare namespace TsDataForgeInternals {
639
647
  type MergeTwo<A extends UnknownRecord, B extends UnknownRecord> = {
640
648
  // 1. Required properties from B (override A completely)
641
649
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
642
- readonly [K in keyof B as {} extends Pick<B, K> ? never : K]: B[K];
650
+ readonly [K in keyof B as {} extends StrictPick<B, K> ? never : K]: B[K];
643
651
  } & {
644
652
  // 2. Optional properties from B that are NOT in A
645
653
  readonly [
646
654
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
647
- K in keyof B as {} extends Pick<B, K>
655
+ K in keyof B as {} extends StrictPick<B, K>
648
656
  ? K extends keyof A
649
657
  ? never
650
658
  : K
@@ -654,7 +662,7 @@ declare namespace TsDataForgeInternals {
654
662
  // 3. Optional properties from B that ARE in A (create union)
655
663
  readonly [
656
664
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
657
- K in keyof B as {} extends Pick<B, K>
665
+ K in keyof B as {} extends StrictPick<B, K>
658
666
  ? K extends keyof A
659
667
  ? K
660
668
  : never
@@ -664,22 +672,26 @@ declare namespace TsDataForgeInternals {
664
672
  // 4. Required properties only in A (not overridden by B)
665
673
  readonly [
666
674
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
667
- K in Exclude<keyof A, keyof B> as {} extends Pick<A, K> ? never : K
675
+ K in RelaxedExclude<keyof A, keyof B> as {} extends StrictPick<A, K>
676
+ ? never
677
+ : K
668
678
  ]: A[K];
669
679
  } & {
670
680
  // 5. Optional properties only in A (not overridden by B)
671
681
  readonly [
672
682
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
673
- K in Exclude<keyof A, keyof B> as {} extends Pick<A, K> ? K : never
683
+ K in RelaxedExclude<keyof A, keyof B> as {} extends StrictPick<A, K>
684
+ ? K
685
+ : never
674
686
  ]?: A[K];
675
687
  } extends infer O
676
688
  ? Readonly<
677
689
  {
678
690
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
679
- [K in keyof O as {} extends Pick<O, K> ? never : K]: O[K];
691
+ [K in keyof O as {} extends StrictPick<O, K> ? never : K]: O[K];
680
692
  } & {
681
693
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
682
- [K in keyof O as {} extends Pick<O, K> ? K : never]?: O[K];
694
+ [K in keyof O as {} extends StrictPick<O, K> ? K : never]?: O[K];
683
695
  }
684
696
  >
685
697
  : never;
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  type DeepReadonly,
3
3
  type FixedLengthTuple,
4
+ type ReadonlyRecord,
4
5
  type UnknownRecord,
5
6
  } from 'ts-type-forge';
6
7
  import { expectType } from '../expect-type.mjs';
@@ -180,7 +181,7 @@ describe('fromEntries', () => {
180
181
  ] as const;
181
182
 
182
183
  const result = Obj.fromEntries(dynamicEntries) satisfies Partial<
183
- Readonly<Record<'name' | 'email', string>>
184
+ ReadonlyRecord<'name' | 'email', string>
184
185
  >;
185
186
 
186
187
  assert.deepStrictEqual(result, { name: 'Alice' });
@@ -39,9 +39,11 @@ export const fastDeepEqual = <T,>(a: T, b: T): boolean => {
39
39
  return (
40
40
  isMap(b) &&
41
41
  a.size === b.size &&
42
- Array.from(a.entries()).every(
43
- ([key, value]) => b.has(key) && fastDeepEqual(value, b.get(key)),
44
- )
42
+ a
43
+ .entries()
44
+ .every(
45
+ ([key, value]) => b.has(key) && fastDeepEqual(value, b.get(key)),
46
+ )
45
47
  );
46
48
  }
47
49
 
@@ -49,7 +51,7 @@ export const fastDeepEqual = <T,>(a: T, b: T): boolean => {
49
51
  return (
50
52
  isSet(b) &&
51
53
  a.size === b.size &&
52
- Array.from(a.entries()).every(([value]) => b.has(value))
54
+ a.entries().every(([value]) => b.has(value))
53
55
  );
54
56
  }
55
57