ts-data-forge 6.8.0 → 6.9.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 (53) hide show
  1. package/dist/array/impl/array-utils-set-op.d.mts.map +1 -1
  2. package/dist/array/impl/array-utils-set-op.mjs +1 -3
  3. package/dist/array/impl/array-utils-set-op.mjs.map +1 -1
  4. package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
  5. package/dist/array/impl/array-utils-validation.mjs +2 -6
  6. package/dist/array/impl/array-utils-validation.mjs.map +1 -1
  7. package/dist/object/object.d.mts +52 -0
  8. package/dist/object/object.d.mts.map +1 -1
  9. package/dist/object/object.mjs +103 -1
  10. package/dist/object/object.mjs.map +1 -1
  11. package/dist/others/fast-deep-equal.d.mts.map +1 -1
  12. package/dist/others/fast-deep-equal.mjs +0 -1
  13. package/dist/others/fast-deep-equal.mjs.map +1 -1
  14. package/package.json +20 -20
  15. package/src/array/impl/array-utils-overload-type-error.test.mts +2 -4
  16. package/src/array/impl/array-utils-set-op.mts +0 -1
  17. package/src/array/impl/array-utils-validation.mts +2 -6
  18. package/src/functional/optional.test.mts +5 -7
  19. package/src/functional/result.test.mts +8 -10
  20. package/src/functional/ternary-result.test.mts +4 -4
  21. package/src/json/json.test.mts +5 -5
  22. package/src/number/branded-types/finite-number.test.mts +11 -15
  23. package/src/number/branded-types/int.test.mts +13 -13
  24. package/src/number/branded-types/int16.test.mts +15 -15
  25. package/src/number/branded-types/int32.test.mts +15 -15
  26. package/src/number/branded-types/non-negative-finite-number.test.mts +15 -19
  27. package/src/number/branded-types/non-negative-int16.test.mts +15 -15
  28. package/src/number/branded-types/non-negative-int32.test.mts +15 -15
  29. package/src/number/branded-types/non-zero-finite-number.test.mts +18 -18
  30. package/src/number/branded-types/non-zero-int.test.mts +14 -18
  31. package/src/number/branded-types/non-zero-int16.test.mts +15 -19
  32. package/src/number/branded-types/non-zero-int32.test.mts +15 -19
  33. package/src/number/branded-types/non-zero-safe-int.test.mts +18 -22
  34. package/src/number/branded-types/non-zero-uint16.test.mts +15 -15
  35. package/src/number/branded-types/non-zero-uint32.test.mts +15 -15
  36. package/src/number/branded-types/positive-finite-number.test.mts +18 -18
  37. package/src/number/branded-types/positive-int.test.mts +16 -22
  38. package/src/number/branded-types/positive-int16.test.mts +14 -14
  39. package/src/number/branded-types/positive-int32.test.mts +14 -14
  40. package/src/number/branded-types/positive-safe-int.test.mts +18 -20
  41. package/src/number/branded-types/positive-uint16.test.mts +15 -15
  42. package/src/number/branded-types/positive-uint32.test.mts +15 -15
  43. package/src/number/branded-types/safe-int.test.mts +17 -21
  44. package/src/number/branded-types/safe-uint.test.mts +16 -22
  45. package/src/number/branded-types/uint.test.mts +14 -14
  46. package/src/number/branded-types/uint16.test.mts +14 -14
  47. package/src/number/branded-types/uint32.test.mts +14 -14
  48. package/src/number/enum/int8.test.mts +1 -1
  49. package/src/number/enum/uint8.test.mts +1 -1
  50. package/src/object/object.mts +170 -1
  51. package/src/object/object.test.mts +172 -0
  52. package/src/others/fast-deep-equal.mts +0 -1
  53. package/src/others/unknown-to-string.test.mts +1 -1
@@ -9,45 +9,45 @@ import {
9
9
  describe('PositiveUint16 test', () => {
10
10
  describe(asPositiveUint16, () => {
11
11
  test('accepts valid positive uint16 values', () => {
12
- expect(() => asPositiveUint16(1)).not.toThrowError();
12
+ expect(() => asPositiveUint16(1)).not.toThrow();
13
13
 
14
- expect(() => asPositiveUint16(1000)).not.toThrowError();
14
+ expect(() => asPositiveUint16(1000)).not.toThrow();
15
15
 
16
- expect(() => asPositiveUint16(65_535)).not.toThrowError(); // 2^16 - 1
16
+ expect(() => asPositiveUint16(65_535)).not.toThrow(); // 2^16 - 1
17
17
 
18
- expect(() => asPositiveUint16(32_768)).not.toThrowError(); // 2^15
18
+ expect(() => asPositiveUint16(32_768)).not.toThrow(); // 2^15
19
19
  });
20
20
 
21
21
  test('rejects zero', () => {
22
- expect(() => asPositiveUint16(0)).toThrowError(TypeError);
22
+ expect(() => asPositiveUint16(0)).toThrow(TypeError);
23
23
  });
24
24
 
25
25
  test('rejects values outside uint16 range', () => {
26
- expect(() => asPositiveUint16(65_536)).toThrowError(TypeError); // 2^16
26
+ expect(() => asPositiveUint16(65_536)).toThrow(TypeError); // 2^16
27
27
 
28
- expect(() => asPositiveUint16(100_000)).toThrowError(TypeError);
28
+ expect(() => asPositiveUint16(100_000)).toThrow(TypeError);
29
29
  });
30
30
 
31
31
  test('rejects negative integers', () => {
32
- expect(() => asPositiveUint16(-1)).toThrowError(TypeError);
32
+ expect(() => asPositiveUint16(-1)).toThrow(TypeError);
33
33
 
34
- expect(() => asPositiveUint16(-42)).toThrowError(TypeError);
34
+ expect(() => asPositiveUint16(-42)).toThrow(TypeError);
35
35
  });
36
36
 
37
37
  test('rejects non-integers', () => {
38
- expect(() => asPositiveUint16(Number.NaN)).toThrowError(TypeError);
38
+ expect(() => asPositiveUint16(Number.NaN)).toThrow(TypeError);
39
39
 
40
- expect(() => asPositiveUint16(Number.POSITIVE_INFINITY)).toThrowError(
40
+ expect(() => asPositiveUint16(Number.POSITIVE_INFINITY)).toThrow(
41
41
  TypeError,
42
42
  );
43
43
 
44
- expect(() => asPositiveUint16(Number.NEGATIVE_INFINITY)).toThrowError(
44
+ expect(() => asPositiveUint16(Number.NEGATIVE_INFINITY)).toThrow(
45
45
  TypeError,
46
46
  );
47
47
 
48
- expect(() => asPositiveUint16(1.2)).toThrowError(TypeError);
48
+ expect(() => asPositiveUint16(1.2)).toThrow(TypeError);
49
49
 
50
- expect(() => asPositiveUint16(-3.4)).toThrowError(TypeError);
50
+ expect(() => asPositiveUint16(-3.4)).toThrow(TypeError);
51
51
  });
52
52
 
53
53
  test('returns the same value for valid inputs', () => {
@@ -70,7 +70,7 @@ describe('PositiveUint16 test', () => {
70
70
  ] as const)(
71
71
  `asPositiveUint16($name) should throw a TypeError`,
72
72
  ({ value }) => {
73
- expect(() => asPositiveUint16(value)).toThrowError(
73
+ expect(() => asPositiveUint16(value)).toThrow(
74
74
  new TypeError(
75
75
  `Expected a positive integer in [1, 2^16), got: ${value}`,
76
76
  ),
@@ -9,45 +9,45 @@ import {
9
9
  describe('PositiveUint32 test', () => {
10
10
  describe(asPositiveUint32, () => {
11
11
  test('accepts valid positive uint32 values', () => {
12
- expect(() => asPositiveUint32(1)).not.toThrowError();
12
+ expect(() => asPositiveUint32(1)).not.toThrow();
13
13
 
14
- expect(() => asPositiveUint32(1000)).not.toThrowError();
14
+ expect(() => asPositiveUint32(1000)).not.toThrow();
15
15
 
16
- expect(() => asPositiveUint32(4_294_967_295)).not.toThrowError(); // 2^32 - 1
16
+ expect(() => asPositiveUint32(4_294_967_295)).not.toThrow(); // 2^32 - 1
17
17
 
18
- expect(() => asPositiveUint32(2_147_483_648)).not.toThrowError(); // 2^31
18
+ expect(() => asPositiveUint32(2_147_483_648)).not.toThrow(); // 2^31
19
19
  });
20
20
 
21
21
  test('rejects zero', () => {
22
- expect(() => asPositiveUint32(0)).toThrowError(TypeError);
22
+ expect(() => asPositiveUint32(0)).toThrow(TypeError);
23
23
  });
24
24
 
25
25
  test('rejects values outside uint32 range', () => {
26
- expect(() => asPositiveUint32(4_294_967_296)).toThrowError(TypeError); // 2^32
26
+ expect(() => asPositiveUint32(4_294_967_296)).toThrow(TypeError); // 2^32
27
27
 
28
- expect(() => asPositiveUint32(10_000_000_000)).toThrowError(TypeError);
28
+ expect(() => asPositiveUint32(10_000_000_000)).toThrow(TypeError);
29
29
  });
30
30
 
31
31
  test('rejects negative integers', () => {
32
- expect(() => asPositiveUint32(-1)).toThrowError(TypeError);
32
+ expect(() => asPositiveUint32(-1)).toThrow(TypeError);
33
33
 
34
- expect(() => asPositiveUint32(-42)).toThrowError(TypeError);
34
+ expect(() => asPositiveUint32(-42)).toThrow(TypeError);
35
35
  });
36
36
 
37
37
  test('rejects non-integers', () => {
38
- expect(() => asPositiveUint32(Number.NaN)).toThrowError(TypeError);
38
+ expect(() => asPositiveUint32(Number.NaN)).toThrow(TypeError);
39
39
 
40
- expect(() => asPositiveUint32(Number.POSITIVE_INFINITY)).toThrowError(
40
+ expect(() => asPositiveUint32(Number.POSITIVE_INFINITY)).toThrow(
41
41
  TypeError,
42
42
  );
43
43
 
44
- expect(() => asPositiveUint32(Number.NEGATIVE_INFINITY)).toThrowError(
44
+ expect(() => asPositiveUint32(Number.NEGATIVE_INFINITY)).toThrow(
45
45
  TypeError,
46
46
  );
47
47
 
48
- expect(() => asPositiveUint32(1.2)).toThrowError(TypeError);
48
+ expect(() => asPositiveUint32(1.2)).toThrow(TypeError);
49
49
 
50
- expect(() => asPositiveUint32(-3.4)).toThrowError(TypeError);
50
+ expect(() => asPositiveUint32(-3.4)).toThrow(TypeError);
51
51
  });
52
52
 
53
53
  test('returns the same value for valid inputs', () => {
@@ -70,7 +70,7 @@ describe('PositiveUint32 test', () => {
70
70
  ] as const)(
71
71
  `asPositiveUint32($name) should throw a TypeError`,
72
72
  ({ value }) => {
73
- expect(() => asPositiveUint32(value)).toThrowError(
73
+ expect(() => asPositiveUint32(value)).toThrow(
74
74
  new TypeError(
75
75
  `Expected a positive integer in [1, 2^32), got: ${value}`,
76
76
  ),
@@ -5,45 +5,41 @@ import { asSafeInt, SafeInt } from './safe-int.mjs';
5
5
  describe('SafeInt test', () => {
6
6
  describe(asSafeInt, () => {
7
7
  test('accepts valid safe integers', () => {
8
- expect(() => asSafeInt(0)).not.toThrowError();
8
+ expect(() => asSafeInt(0)).not.toThrow();
9
9
 
10
- expect(() => asSafeInt(1)).not.toThrowError();
10
+ expect(() => asSafeInt(1)).not.toThrow();
11
11
 
12
- expect(() => asSafeInt(-1)).not.toThrowError();
12
+ expect(() => asSafeInt(-1)).not.toThrow();
13
13
 
14
- expect(() => asSafeInt(42)).not.toThrowError();
14
+ expect(() => asSafeInt(42)).not.toThrow();
15
15
 
16
- expect(() => asSafeInt(-42)).not.toThrowError();
16
+ expect(() => asSafeInt(-42)).not.toThrow();
17
17
 
18
- expect(() => asSafeInt(Number.MAX_SAFE_INTEGER)).not.toThrowError();
18
+ expect(() => asSafeInt(Number.MAX_SAFE_INTEGER)).not.toThrow();
19
19
 
20
- expect(() => asSafeInt(Number.MIN_SAFE_INTEGER)).not.toThrowError();
20
+ expect(() => asSafeInt(Number.MIN_SAFE_INTEGER)).not.toThrow();
21
21
  });
22
22
 
23
23
  test('rejects values outside safe integer range', () => {
24
- expect(() => asSafeInt(Number.MAX_SAFE_INTEGER + 1)).toThrowError(
25
- TypeError,
26
- );
24
+ expect(() => asSafeInt(Number.MAX_SAFE_INTEGER + 1)).toThrow(TypeError);
27
25
 
28
- expect(() => asSafeInt(Number.MIN_SAFE_INTEGER - 1)).toThrowError(
29
- TypeError,
30
- );
26
+ expect(() => asSafeInt(Number.MIN_SAFE_INTEGER - 1)).toThrow(TypeError);
31
27
 
32
- expect(() => asSafeInt(Number.MAX_VALUE)).toThrowError(TypeError);
28
+ expect(() => asSafeInt(Number.MAX_VALUE)).toThrow(TypeError);
33
29
 
34
- expect(() => asSafeInt(-Number.MAX_VALUE)).toThrowError(TypeError);
30
+ expect(() => asSafeInt(-Number.MAX_VALUE)).toThrow(TypeError);
35
31
  });
36
32
 
37
33
  test('rejects non-integers', () => {
38
- expect(() => asSafeInt(Number.NaN)).toThrowError(TypeError);
34
+ expect(() => asSafeInt(Number.NaN)).toThrow(TypeError);
39
35
 
40
- expect(() => asSafeInt(Number.POSITIVE_INFINITY)).toThrowError(TypeError);
36
+ expect(() => asSafeInt(Number.POSITIVE_INFINITY)).toThrow(TypeError);
41
37
 
42
- expect(() => asSafeInt(Number.NEGATIVE_INFINITY)).toThrowError(TypeError);
38
+ expect(() => asSafeInt(Number.NEGATIVE_INFINITY)).toThrow(TypeError);
43
39
 
44
- expect(() => asSafeInt(1.2)).toThrowError(TypeError);
40
+ expect(() => asSafeInt(1.2)).toThrow(TypeError);
45
41
 
46
- expect(() => asSafeInt(-3.4)).toThrowError(TypeError);
42
+ expect(() => asSafeInt(-3.4)).toThrow(TypeError);
47
43
  });
48
44
 
49
45
  test('returns the same value for valid inputs', () => {
@@ -65,7 +61,7 @@ describe('SafeInt test', () => {
65
61
  { name: '1.2', value: 1.2 },
66
62
  { name: '-3.4', value: -3.4 },
67
63
  ] as const)(`asSafeInt($name) should throw a TypeError`, ({ value }) => {
68
- expect(() => asSafeInt(value)).toThrowError(
64
+ expect(() => asSafeInt(value)).toThrow(
69
65
  new TypeError(`Expected a safe integer, got: ${value}`),
70
66
  );
71
67
  });
@@ -5,47 +5,41 @@ import { asSafeUint, isSafeUint, SafeUint } from './safe-uint.mjs';
5
5
  describe('SafeUint test', () => {
6
6
  describe(asSafeUint, () => {
7
7
  test('accepts valid safe unsigned integers', () => {
8
- expect(() => asSafeUint(0)).not.toThrowError();
8
+ expect(() => asSafeUint(0)).not.toThrow();
9
9
 
10
- expect(() => asSafeUint(1)).not.toThrowError();
10
+ expect(() => asSafeUint(1)).not.toThrow();
11
11
 
12
- expect(() => asSafeUint(42)).not.toThrowError();
12
+ expect(() => asSafeUint(42)).not.toThrow();
13
13
 
14
- expect(() => asSafeUint(100)).not.toThrowError();
14
+ expect(() => asSafeUint(100)).not.toThrow();
15
15
 
16
- expect(() => asSafeUint(Number.MAX_SAFE_INTEGER)).not.toThrowError();
16
+ expect(() => asSafeUint(Number.MAX_SAFE_INTEGER)).not.toThrow();
17
17
  });
18
18
 
19
19
  test('rejects negative numbers', () => {
20
- expect(() => asSafeUint(-1)).toThrowError(TypeError);
20
+ expect(() => asSafeUint(-1)).toThrow(TypeError);
21
21
 
22
- expect(() => asSafeUint(-42)).toThrowError(TypeError);
22
+ expect(() => asSafeUint(-42)).toThrow(TypeError);
23
23
 
24
- expect(() => asSafeUint(Number.MIN_SAFE_INTEGER)).toThrowError(TypeError);
24
+ expect(() => asSafeUint(Number.MIN_SAFE_INTEGER)).toThrow(TypeError);
25
25
  });
26
26
 
27
27
  test('rejects values outside safe integer range', () => {
28
- expect(() => asSafeUint(Number.MAX_SAFE_INTEGER + 1)).toThrowError(
29
- TypeError,
30
- );
28
+ expect(() => asSafeUint(Number.MAX_SAFE_INTEGER + 1)).toThrow(TypeError);
31
29
 
32
- expect(() => asSafeUint(Number.MAX_VALUE)).toThrowError(TypeError);
30
+ expect(() => asSafeUint(Number.MAX_VALUE)).toThrow(TypeError);
33
31
  });
34
32
 
35
33
  test('rejects non-integers', () => {
36
- expect(() => asSafeUint(Number.NaN)).toThrowError(TypeError);
34
+ expect(() => asSafeUint(Number.NaN)).toThrow(TypeError);
37
35
 
38
- expect(() => asSafeUint(Number.POSITIVE_INFINITY)).toThrowError(
39
- TypeError,
40
- );
36
+ expect(() => asSafeUint(Number.POSITIVE_INFINITY)).toThrow(TypeError);
41
37
 
42
- expect(() => asSafeUint(Number.NEGATIVE_INFINITY)).toThrowError(
43
- TypeError,
44
- );
38
+ expect(() => asSafeUint(Number.NEGATIVE_INFINITY)).toThrow(TypeError);
45
39
 
46
- expect(() => asSafeUint(1.2)).toThrowError(TypeError);
40
+ expect(() => asSafeUint(1.2)).toThrow(TypeError);
47
41
 
48
- expect(() => asSafeUint(-3.4)).toThrowError(TypeError);
42
+ expect(() => asSafeUint(-3.4)).toThrow(TypeError);
49
43
  });
50
44
 
51
45
  test('returns the same value for valid inputs', () => {
@@ -66,7 +60,7 @@ describe('SafeUint test', () => {
66
60
  { name: '-3.4', value: -3.4 },
67
61
  { name: '-1', value: -1 },
68
62
  ] as const)(`asSafeUint($name) should throw a TypeError`, ({ value }) => {
69
- expect(() => asSafeUint(value)).toThrowError(
63
+ expect(() => asSafeUint(value)).toThrow(
70
64
  new TypeError(`Expected a non-negative safe integer, got: ${value}`),
71
65
  );
72
66
  });
@@ -5,35 +5,35 @@ import { asUint, isUint, Uint } from './uint.mjs';
5
5
  describe('Uint test', () => {
6
6
  describe(asUint, () => {
7
7
  test('accepts valid unsigned integers', () => {
8
- expect(() => asUint(0)).not.toThrowError();
8
+ expect(() => asUint(0)).not.toThrow();
9
9
 
10
- expect(() => asUint(1)).not.toThrowError();
10
+ expect(() => asUint(1)).not.toThrow();
11
11
 
12
- expect(() => asUint(42)).not.toThrowError();
12
+ expect(() => asUint(42)).not.toThrow();
13
13
 
14
- expect(() => asUint(100)).not.toThrowError();
14
+ expect(() => asUint(100)).not.toThrow();
15
15
 
16
- expect(() => asUint(Number.MAX_SAFE_INTEGER)).not.toThrowError();
16
+ expect(() => asUint(Number.MAX_SAFE_INTEGER)).not.toThrow();
17
17
  });
18
18
 
19
19
  test('rejects negative integers', () => {
20
- expect(() => asUint(-1)).toThrowError(TypeError);
20
+ expect(() => asUint(-1)).toThrow(TypeError);
21
21
 
22
- expect(() => asUint(-42)).toThrowError(TypeError);
22
+ expect(() => asUint(-42)).toThrow(TypeError);
23
23
 
24
- expect(() => asUint(Number.MIN_SAFE_INTEGER)).toThrowError(TypeError);
24
+ expect(() => asUint(Number.MIN_SAFE_INTEGER)).toThrow(TypeError);
25
25
  });
26
26
 
27
27
  test('rejects non-integers', () => {
28
- expect(() => asUint(Number.NaN)).toThrowError(TypeError);
28
+ expect(() => asUint(Number.NaN)).toThrow(TypeError);
29
29
 
30
- expect(() => asUint(Number.POSITIVE_INFINITY)).toThrowError(TypeError);
30
+ expect(() => asUint(Number.POSITIVE_INFINITY)).toThrow(TypeError);
31
31
 
32
- expect(() => asUint(Number.NEGATIVE_INFINITY)).toThrowError(TypeError);
32
+ expect(() => asUint(Number.NEGATIVE_INFINITY)).toThrow(TypeError);
33
33
 
34
- expect(() => asUint(1.2)).toThrowError(TypeError);
34
+ expect(() => asUint(1.2)).toThrow(TypeError);
35
35
 
36
- expect(() => asUint(-3.4)).toThrowError(TypeError);
36
+ expect(() => asUint(-3.4)).toThrow(TypeError);
37
37
  });
38
38
 
39
39
  test('returns the same value for valid inputs', () => {
@@ -52,7 +52,7 @@ describe('Uint test', () => {
52
52
  { name: '-3.4', value: -3.4 },
53
53
  { name: '-1', value: -1 },
54
54
  ] as const)(`asUint($name) should throw a TypeError`, ({ value }) => {
55
- expect(() => asUint(value)).toThrowError(
55
+ expect(() => asUint(value)).toThrow(
56
56
  new TypeError(`Expected a non-negative integer, got: ${value}`),
57
57
  );
58
58
  });
@@ -6,37 +6,37 @@ import { asUint16, isUint16, Uint16 } from './uint16.mjs';
6
6
  describe('Uint16 test', () => {
7
7
  describe(asUint16, () => {
8
8
  test('accepts valid uint16 values', () => {
9
- expect(() => asUint16(0)).not.toThrowError();
9
+ expect(() => asUint16(0)).not.toThrow();
10
10
 
11
- expect(() => asUint16(1)).not.toThrowError();
11
+ expect(() => asUint16(1)).not.toThrow();
12
12
 
13
- expect(() => asUint16(65_535)).not.toThrowError(); // 2^16 - 1
13
+ expect(() => asUint16(65_535)).not.toThrow(); // 2^16 - 1
14
14
 
15
- expect(() => asUint16(32_768)).not.toThrowError(); // 2^15
15
+ expect(() => asUint16(32_768)).not.toThrow(); // 2^15
16
16
  });
17
17
 
18
18
  test('rejects values outside uint16 range', () => {
19
- expect(() => asUint16(65_536)).toThrowError(TypeError); // 2^16
19
+ expect(() => asUint16(65_536)).toThrow(TypeError); // 2^16
20
20
 
21
- expect(() => asUint16(100_000)).toThrowError(TypeError);
21
+ expect(() => asUint16(100_000)).toThrow(TypeError);
22
22
  });
23
23
 
24
24
  test('rejects negative integers', () => {
25
- expect(() => asUint16(-1)).toThrowError(TypeError);
25
+ expect(() => asUint16(-1)).toThrow(TypeError);
26
26
 
27
- expect(() => asUint16(-42)).toThrowError(TypeError);
27
+ expect(() => asUint16(-42)).toThrow(TypeError);
28
28
  });
29
29
 
30
30
  test('rejects non-integers', () => {
31
- expect(() => asUint16(Number.NaN)).toThrowError(TypeError);
31
+ expect(() => asUint16(Number.NaN)).toThrow(TypeError);
32
32
 
33
- expect(() => asUint16(Number.POSITIVE_INFINITY)).toThrowError(TypeError);
33
+ expect(() => asUint16(Number.POSITIVE_INFINITY)).toThrow(TypeError);
34
34
 
35
- expect(() => asUint16(Number.NEGATIVE_INFINITY)).toThrowError(TypeError);
35
+ expect(() => asUint16(Number.NEGATIVE_INFINITY)).toThrow(TypeError);
36
36
 
37
- expect(() => asUint16(1.2)).toThrowError(TypeError);
37
+ expect(() => asUint16(1.2)).toThrow(TypeError);
38
38
 
39
- expect(() => asUint16(-3.4)).toThrowError(TypeError);
39
+ expect(() => asUint16(-3.4)).toThrow(TypeError);
40
40
  });
41
41
 
42
42
  test('returns the same value for valid inputs', () => {
@@ -55,7 +55,7 @@ describe('Uint16 test', () => {
55
55
  { name: '-3.4', value: -3.4 },
56
56
  { name: '-1', value: -1 },
57
57
  ] as const)(`asUint16($name) should throw a TypeError`, ({ value }) => {
58
- expect(() => asUint16(value)).toThrowError(
58
+ expect(() => asUint16(value)).toThrow(
59
59
  new TypeError(
60
60
  `Expected a non-negative integer less than 2^16, got: ${value}`,
61
61
  ),
@@ -6,37 +6,37 @@ import { asUint32, isUint32, Uint32 } from './uint32.mjs';
6
6
  describe('Uint32 test', () => {
7
7
  describe(asUint32, () => {
8
8
  test('accepts valid uint32 values', () => {
9
- expect(() => asUint32(0)).not.toThrowError();
9
+ expect(() => asUint32(0)).not.toThrow();
10
10
 
11
- expect(() => asUint32(1)).not.toThrowError();
11
+ expect(() => asUint32(1)).not.toThrow();
12
12
 
13
- expect(() => asUint32(4_294_967_295)).not.toThrowError(); // 2^32 - 1
13
+ expect(() => asUint32(4_294_967_295)).not.toThrow(); // 2^32 - 1
14
14
 
15
- expect(() => asUint32(2_147_483_648)).not.toThrowError(); // 2^31
15
+ expect(() => asUint32(2_147_483_648)).not.toThrow(); // 2^31
16
16
  });
17
17
 
18
18
  test('rejects values outside uint32 range', () => {
19
- expect(() => asUint32(4_294_967_296)).toThrowError(TypeError); // 2^32
19
+ expect(() => asUint32(4_294_967_296)).toThrow(TypeError); // 2^32
20
20
 
21
- expect(() => asUint32(10_000_000_000)).toThrowError(TypeError);
21
+ expect(() => asUint32(10_000_000_000)).toThrow(TypeError);
22
22
  });
23
23
 
24
24
  test('rejects negative integers', () => {
25
- expect(() => asUint32(-1)).toThrowError(TypeError);
25
+ expect(() => asUint32(-1)).toThrow(TypeError);
26
26
 
27
- expect(() => asUint32(-42)).toThrowError(TypeError);
27
+ expect(() => asUint32(-42)).toThrow(TypeError);
28
28
  });
29
29
 
30
30
  test('rejects non-integers', () => {
31
- expect(() => asUint32(Number.NaN)).toThrowError(TypeError);
31
+ expect(() => asUint32(Number.NaN)).toThrow(TypeError);
32
32
 
33
- expect(() => asUint32(Number.POSITIVE_INFINITY)).toThrowError(TypeError);
33
+ expect(() => asUint32(Number.POSITIVE_INFINITY)).toThrow(TypeError);
34
34
 
35
- expect(() => asUint32(Number.NEGATIVE_INFINITY)).toThrowError(TypeError);
35
+ expect(() => asUint32(Number.NEGATIVE_INFINITY)).toThrow(TypeError);
36
36
 
37
- expect(() => asUint32(1.2)).toThrowError(TypeError);
37
+ expect(() => asUint32(1.2)).toThrow(TypeError);
38
38
 
39
- expect(() => asUint32(-3.4)).toThrowError(TypeError);
39
+ expect(() => asUint32(-3.4)).toThrow(TypeError);
40
40
  });
41
41
 
42
42
  test('returns the same value for valid inputs', () => {
@@ -55,7 +55,7 @@ describe('Uint32 test', () => {
55
55
  { name: '-3.4', value: -3.4 },
56
56
  { name: '-1', value: -1 },
57
57
  ] as const)(`asUint32($name) should throw a TypeError`, ({ value }) => {
58
- expect(() => asUint32(value)).toThrowError(
58
+ expect(() => asUint32(value)).toThrow(
59
59
  new TypeError(
60
60
  `Expected a non-negative integer less than 2^32, got: ${value}`,
61
61
  ),
@@ -35,7 +35,7 @@ describe('Int8 test', () => {
35
35
  { value: Number.POSITIVE_INFINITY, name: 'Infinity' },
36
36
  { value: Number.NEGATIVE_INFINITY, name: '-Infinity' },
37
37
  ])('asInt8($name) should throw TypeError', ({ value }) => {
38
- expect(() => asInt8(value)).toThrowError(TypeError);
38
+ expect(() => asInt8(value)).toThrow(TypeError);
39
39
  });
40
40
  });
41
41
 
@@ -35,7 +35,7 @@ describe('Uint8 test', () => {
35
35
  { value: Number.POSITIVE_INFINITY, name: 'Infinity' },
36
36
  { value: Number.NEGATIVE_INFINITY, name: '-Infinity' },
37
37
  ])('asUint8($name) should throw TypeError', ({ value }) => {
38
- expect(() => asUint8(value)).toThrowError(TypeError);
38
+ expect(() => asUint8(value)).toThrow(TypeError);
39
39
  });
40
40
  });
41
41
 
@@ -1,3 +1,6 @@
1
+ import { Arr } from '../array/index.mjs';
2
+ import { hasKey, isRecord } from '../guard/index.mjs';
3
+
1
4
  /**
2
5
  * A collection of type-safe object utility functions providing functional
3
6
  * programming patterns for object manipulation, including pick, omit, shallow
@@ -58,7 +61,6 @@ export namespace Obj {
58
61
 
59
62
  const bEntries = Object.entries(b);
60
63
 
61
- // eslint-disable-next-line ts-data-forge/prefer-arr-is-array-of-length
62
64
  if (aEntries.length !== bEntries.length) return false;
63
65
 
64
66
  return aEntries.every(([k, v]) => eq(b[k], v));
@@ -332,8 +334,175 @@ export namespace Obj {
332
334
  ): TsDataForgeInternals.MergeAll<Records> =>
333
335
  // eslint-disable-next-line total-functions/no-unsafe-type-assertion
334
336
  Object.fromEntries(records.flatMap((r) => Object.entries(r))) as never;
337
+
338
+ /**
339
+ * Deeply picks a nested property from an object along the specified key path.
340
+ * Supports both direct and curried usage.
341
+ *
342
+ * @example
343
+ *
344
+ * ```ts
345
+ * const data = { a: { b: { c: 1, d: 2 }, e: 3 }, f: 4 } as const;
346
+ *
347
+ * // Direct usage
348
+ * const result = Obj.deepPick(data, ['a', 'b', 'c']);
349
+ * assert.deepStrictEqual(result, { a: { b: { c: 1 } } });
350
+ *
351
+ * // Curried usage with pipe
352
+ * const pickName = Obj.deepPick(['user', 'name']);
353
+ * const result2 = pipe(data).map(pickName).value;
354
+ * ```
355
+ *
356
+ * @template R - The type of the input record
357
+ * @template Path - The key path tuple
358
+ * @param record - The source record
359
+ * @param path - A readonly tuple of keys representing the nested path
360
+ * @returns A new record containing only the nested property at the path
361
+ */
362
+ export function deepPick<
363
+ const R extends UnknownRecord,
364
+ const Path extends readonly (string | number)[],
365
+ >(record: R, path: Path): DeepPick<R, Path>;
366
+
367
+ // Curried version
368
+ export function deepPick<const Path extends readonly (string | number)[]>(
369
+ path: Path,
370
+ ): <const R extends UnknownRecord>(record: R) => DeepPick<R, Path>;
371
+
372
+ export function deepPick<
373
+ const R extends UnknownRecord,
374
+ const Path extends readonly (string | number)[],
375
+ >(
376
+ ...args: readonly [record: R, path: Path] | readonly [path: Path]
377
+ ): DeepPick<R, Path> | ((record: R) => DeepPick<R, Path>) {
378
+ switch (args.length) {
379
+ case 2: {
380
+ const [record, path] = args;
381
+
382
+ return (
383
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
384
+ deepPickImpl(record, path) as never
385
+ );
386
+ }
387
+
388
+ case 1: {
389
+ const [path] = args;
390
+
391
+ return (record: R) => deepPick(record, path);
392
+ }
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Deeply omits a nested property from an object along the specified key path.
398
+ * Supports both direct and curried usage.
399
+ *
400
+ * @example
401
+ *
402
+ * ```ts
403
+ * const data = { a: { b: { c: 1, d: 2 }, e: 3 }, f: 4 } as const;
404
+ *
405
+ * // Direct usage
406
+ * const result = Obj.deepOmit(data, ['a', 'b', 'c']);
407
+ * assert.deepStrictEqual(result, { a: { b: { d: 2 }, e: 3 }, f: 4 });
408
+ *
409
+ * // Curried usage with pipe
410
+ * const omitPassword = Obj.deepOmit(['user', 'password']);
411
+ * const result2 = pipe(data).map(omitPassword).value;
412
+ * ```
413
+ *
414
+ * @template R - The type of the input record
415
+ * @template Path - The key path tuple
416
+ * @param record - The source record
417
+ * @param path - A readonly tuple of keys representing the nested path to omit
418
+ * @returns A new record with the nested property at the path removed
419
+ */
420
+ export function deepOmit<
421
+ const R extends UnknownRecord,
422
+ const Path extends readonly (string | number)[],
423
+ >(record: R, path: Path): DeepOmit<R, Path>;
424
+
425
+ // Curried version
426
+ export function deepOmit<const Path extends readonly (string | number)[]>(
427
+ path: Path,
428
+ ): <const R extends UnknownRecord>(record: R) => DeepOmit<R, Path>;
429
+
430
+ export function deepOmit<
431
+ const R extends UnknownRecord,
432
+ const Path extends readonly (string | number)[],
433
+ >(
434
+ ...args: readonly [record: R, path: Path] | readonly [path: Path]
435
+ ): DeepOmit<R, Path> | ((record: R) => DeepOmit<R, Path>) {
436
+ switch (args.length) {
437
+ case 2: {
438
+ const [record, path] = args;
439
+
440
+ return (
441
+ // eslint-disable-next-line total-functions/no-unsafe-type-assertion
442
+ deepOmitImpl(record, path) as never
443
+ );
444
+ }
445
+
446
+ case 1: {
447
+ const [path] = args;
448
+
449
+ return (record: R) => deepOmit(record, path);
450
+ }
451
+ }
452
+ }
335
453
  }
336
454
 
455
+ const deepPickImpl = (
456
+ record: UnknownRecord,
457
+ path: readonly (string | number)[],
458
+ ): UnknownRecord => {
459
+ if (!Arr.isNonEmpty(path)) return record;
460
+
461
+ const head = path[0];
462
+
463
+ if (!hasKey(record, head)) return {};
464
+
465
+ const value = record[head];
466
+
467
+ const tail = path.slice(1);
468
+
469
+ if (!Arr.isNonEmpty(tail)) return { [head]: value };
470
+
471
+ if (!isRecord(value)) return { [head]: {} };
472
+
473
+ return {
474
+ [head]: deepPickImpl(value, tail),
475
+ };
476
+ };
477
+
478
+ const deepOmitImpl = (
479
+ record: UnknownRecord,
480
+ path: readonly (string | number)[],
481
+ ): UnknownRecord => {
482
+ if (!Arr.isNonEmpty(path)) return record;
483
+
484
+ const head = path[0];
485
+
486
+ const tail = path.slice(1);
487
+
488
+ if (!hasKey(record, head)) return record;
489
+
490
+ if (!Arr.isNonEmpty(tail)) {
491
+ return Object.fromEntries(
492
+ Object.entries(record).filter(([k]) => k !== head),
493
+ );
494
+ }
495
+
496
+ const value = record[head];
497
+
498
+ if (!isRecord(value)) return record;
499
+
500
+ return {
501
+ ...record,
502
+ [head]: deepOmitImpl(value, tail),
503
+ };
504
+ };
505
+
337
506
  /**
338
507
  * @internal
339
508
  * Internal type utilities for the Obj module.