ts-data-forge 1.0.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 (143) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +534 -0
  3. package/package.json +101 -0
  4. package/src/array/array-utils-creation.test.mts +443 -0
  5. package/src/array/array-utils-modification.test.mts +197 -0
  6. package/src/array/array-utils-overload-type-error.test.mts +149 -0
  7. package/src/array/array-utils-reducing-value.test.mts +425 -0
  8. package/src/array/array-utils-search.test.mts +169 -0
  9. package/src/array/array-utils-set-op.test.mts +335 -0
  10. package/src/array/array-utils-slice-clamped.test.mts +113 -0
  11. package/src/array/array-utils-slicing.test.mts +316 -0
  12. package/src/array/array-utils-transformation.test.mts +790 -0
  13. package/src/array/array-utils-validation.test.mts +492 -0
  14. package/src/array/array-utils.mts +4000 -0
  15. package/src/array/array.test.mts +146 -0
  16. package/src/array/index.mts +2 -0
  17. package/src/array/tuple-utils.mts +519 -0
  18. package/src/array/tuple-utils.test.mts +518 -0
  19. package/src/collections/imap-mapped.mts +801 -0
  20. package/src/collections/imap-mapped.test.mts +860 -0
  21. package/src/collections/imap.mts +651 -0
  22. package/src/collections/imap.test.mts +932 -0
  23. package/src/collections/index.mts +6 -0
  24. package/src/collections/iset-mapped.mts +889 -0
  25. package/src/collections/iset-mapped.test.mts +1187 -0
  26. package/src/collections/iset.mts +682 -0
  27. package/src/collections/iset.test.mts +1084 -0
  28. package/src/collections/queue.mts +390 -0
  29. package/src/collections/queue.test.mts +282 -0
  30. package/src/collections/stack.mts +423 -0
  31. package/src/collections/stack.test.mts +225 -0
  32. package/src/expect-type.mts +206 -0
  33. package/src/functional/index.mts +4 -0
  34. package/src/functional/match.mts +300 -0
  35. package/src/functional/match.test.mts +177 -0
  36. package/src/functional/optional.mts +733 -0
  37. package/src/functional/optional.test.mts +619 -0
  38. package/src/functional/pipe.mts +212 -0
  39. package/src/functional/pipe.test.mts +85 -0
  40. package/src/functional/result.mts +1134 -0
  41. package/src/functional/result.test.mts +777 -0
  42. package/src/globals.d.mts +38 -0
  43. package/src/guard/has-key.mts +119 -0
  44. package/src/guard/has-key.test.mts +219 -0
  45. package/src/guard/index.mts +7 -0
  46. package/src/guard/is-non-empty-string.mts +108 -0
  47. package/src/guard/is-non-empty-string.test.mts +91 -0
  48. package/src/guard/is-non-null-object.mts +106 -0
  49. package/src/guard/is-non-null-object.test.mts +90 -0
  50. package/src/guard/is-primitive.mts +165 -0
  51. package/src/guard/is-primitive.test.mts +102 -0
  52. package/src/guard/is-record.mts +153 -0
  53. package/src/guard/is-record.test.mts +112 -0
  54. package/src/guard/is-type.mts +450 -0
  55. package/src/guard/is-type.test.mts +496 -0
  56. package/src/guard/key-is-in.mts +163 -0
  57. package/src/guard/key-is-in.test.mts +19 -0
  58. package/src/index.mts +10 -0
  59. package/src/iterator/index.mts +1 -0
  60. package/src/iterator/range.mts +120 -0
  61. package/src/iterator/range.test.mts +33 -0
  62. package/src/json/index.mts +1 -0
  63. package/src/json/json.mts +711 -0
  64. package/src/json/json.test.mts +628 -0
  65. package/src/number/branded-types/finite-number.mts +354 -0
  66. package/src/number/branded-types/finite-number.test.mts +135 -0
  67. package/src/number/branded-types/index.mts +26 -0
  68. package/src/number/branded-types/int.mts +278 -0
  69. package/src/number/branded-types/int.test.mts +140 -0
  70. package/src/number/branded-types/int16.mts +192 -0
  71. package/src/number/branded-types/int16.test.mts +170 -0
  72. package/src/number/branded-types/int32.mts +193 -0
  73. package/src/number/branded-types/int32.test.mts +170 -0
  74. package/src/number/branded-types/non-negative-finite-number.mts +223 -0
  75. package/src/number/branded-types/non-negative-finite-number.test.mts +188 -0
  76. package/src/number/branded-types/non-negative-int16.mts +187 -0
  77. package/src/number/branded-types/non-negative-int16.test.mts +201 -0
  78. package/src/number/branded-types/non-negative-int32.mts +187 -0
  79. package/src/number/branded-types/non-negative-int32.test.mts +204 -0
  80. package/src/number/branded-types/non-zero-finite-number.mts +229 -0
  81. package/src/number/branded-types/non-zero-finite-number.test.mts +198 -0
  82. package/src/number/branded-types/non-zero-int.mts +167 -0
  83. package/src/number/branded-types/non-zero-int.test.mts +177 -0
  84. package/src/number/branded-types/non-zero-int16.mts +196 -0
  85. package/src/number/branded-types/non-zero-int16.test.mts +195 -0
  86. package/src/number/branded-types/non-zero-int32.mts +196 -0
  87. package/src/number/branded-types/non-zero-int32.test.mts +197 -0
  88. package/src/number/branded-types/non-zero-safe-int.mts +196 -0
  89. package/src/number/branded-types/non-zero-safe-int.test.mts +232 -0
  90. package/src/number/branded-types/non-zero-uint16.mts +189 -0
  91. package/src/number/branded-types/non-zero-uint16.test.mts +199 -0
  92. package/src/number/branded-types/non-zero-uint32.mts +189 -0
  93. package/src/number/branded-types/non-zero-uint32.test.mts +199 -0
  94. package/src/number/branded-types/positive-finite-number.mts +241 -0
  95. package/src/number/branded-types/positive-finite-number.test.mts +204 -0
  96. package/src/number/branded-types/positive-int.mts +304 -0
  97. package/src/number/branded-types/positive-int.test.mts +176 -0
  98. package/src/number/branded-types/positive-int16.mts +188 -0
  99. package/src/number/branded-types/positive-int16.test.mts +197 -0
  100. package/src/number/branded-types/positive-int32.mts +188 -0
  101. package/src/number/branded-types/positive-int32.test.mts +197 -0
  102. package/src/number/branded-types/positive-safe-int.mts +187 -0
  103. package/src/number/branded-types/positive-safe-int.test.mts +210 -0
  104. package/src/number/branded-types/positive-uint16.mts +188 -0
  105. package/src/number/branded-types/positive-uint16.test.mts +203 -0
  106. package/src/number/branded-types/positive-uint32.mts +188 -0
  107. package/src/number/branded-types/positive-uint32.test.mts +203 -0
  108. package/src/number/branded-types/safe-int.mts +291 -0
  109. package/src/number/branded-types/safe-int.test.mts +170 -0
  110. package/src/number/branded-types/safe-uint.mts +187 -0
  111. package/src/number/branded-types/safe-uint.test.mts +176 -0
  112. package/src/number/branded-types/uint.mts +179 -0
  113. package/src/number/branded-types/uint.test.mts +158 -0
  114. package/src/number/branded-types/uint16.mts +186 -0
  115. package/src/number/branded-types/uint16.test.mts +170 -0
  116. package/src/number/branded-types/uint32.mts +218 -0
  117. package/src/number/branded-types/uint32.test.mts +170 -0
  118. package/src/number/enum/index.mts +2 -0
  119. package/src/number/enum/int8.mts +344 -0
  120. package/src/number/enum/int8.test.mts +180 -0
  121. package/src/number/enum/uint8.mts +293 -0
  122. package/src/number/enum/uint8.test.mts +164 -0
  123. package/src/number/index.mts +4 -0
  124. package/src/number/num.mts +604 -0
  125. package/src/number/num.test.mts +242 -0
  126. package/src/number/refined-number-utils.mts +566 -0
  127. package/src/object/index.mts +1 -0
  128. package/src/object/object.mts +447 -0
  129. package/src/object/object.test.mts +124 -0
  130. package/src/others/cast-mutable.mts +113 -0
  131. package/src/others/cast-readonly.mts +192 -0
  132. package/src/others/cast-readonly.test.mts +89 -0
  133. package/src/others/if-then.mts +98 -0
  134. package/src/others/if-then.test.mts +75 -0
  135. package/src/others/index.mts +7 -0
  136. package/src/others/map-nullable.mts +172 -0
  137. package/src/others/map-nullable.test.mts +297 -0
  138. package/src/others/memoize-function.mts +196 -0
  139. package/src/others/memoize-function.test.mts +168 -0
  140. package/src/others/tuple.mts +160 -0
  141. package/src/others/tuple.test.mts +11 -0
  142. package/src/others/unknown-to-string.mts +215 -0
  143. package/src/others/unknown-to-string.test.mts +114 -0
@@ -0,0 +1,201 @@
1
+ import { expectType } from '../../expect-type.mjs';
2
+ import {
3
+ asNonNegativeInt16,
4
+ isNonNegativeInt16,
5
+ NonNegativeInt16,
6
+ } from './non-negative-int16.mjs';
7
+ import { asPositiveInt16 } from './positive-int16.mjs';
8
+
9
+ describe('NonNegativeInt16', () => {
10
+ describe('asNonNegativeInt16', () => {
11
+ test('accepts valid non-negative int16 values', () => {
12
+ expect(() => asNonNegativeInt16(0)).not.toThrow();
13
+ expect(() => asNonNegativeInt16(1)).not.toThrow();
14
+ expect(() => asNonNegativeInt16(1000)).not.toThrow();
15
+ expect(() => asNonNegativeInt16(32767)).not.toThrow(); // 2^15 - 1
16
+ });
17
+
18
+ test('rejects negative integers', () => {
19
+ expect(() => asNonNegativeInt16(-1)).toThrow(TypeError);
20
+ expect(() => asNonNegativeInt16(-42)).toThrow(TypeError);
21
+ expect(() => asNonNegativeInt16(-32768)).toThrow(TypeError);
22
+ });
23
+
24
+ test('rejects values outside int16 range', () => {
25
+ expect(() => asNonNegativeInt16(32768)).toThrow(TypeError); // 2^15
26
+ expect(() => asNonNegativeInt16(65536)).toThrow(TypeError);
27
+ });
28
+
29
+ test('rejects non-integers', () => {
30
+ expect(() => asNonNegativeInt16(Number.NaN)).toThrow(TypeError);
31
+ expect(() => asNonNegativeInt16(Number.POSITIVE_INFINITY)).toThrow(
32
+ TypeError,
33
+ );
34
+ expect(() => asNonNegativeInt16(Number.NEGATIVE_INFINITY)).toThrow(
35
+ TypeError,
36
+ );
37
+ expect(() => asNonNegativeInt16(1.2)).toThrow(TypeError);
38
+ expect(() => asNonNegativeInt16(-3.4)).toThrow(TypeError);
39
+ });
40
+
41
+ test('returns the same value for valid inputs', () => {
42
+ expect(asNonNegativeInt16(0)).toBe(0);
43
+ expect(asNonNegativeInt16(5)).toBe(5);
44
+ expect(asNonNegativeInt16(32767)).toBe(32767);
45
+ });
46
+
47
+ test.each([
48
+ { name: 'Number.NaN', value: Number.NaN },
49
+ { name: 'Number.POSITIVE_INFINITY', value: Number.POSITIVE_INFINITY },
50
+ { name: 'Number.NEGATIVE_INFINITY', value: Number.NEGATIVE_INFINITY },
51
+ { name: '1.2', value: 1.2 },
52
+ { name: '-3.4', value: -3.4 },
53
+ { name: '-1', value: -1 },
54
+ { name: '32768', value: 32768 },
55
+ ] as const)(
56
+ `asNonNegativeInt16($name) should throw a TypeError`,
57
+ ({ value }) => {
58
+ expect(() => asNonNegativeInt16(value)).toThrow(
59
+ new TypeError(
60
+ `Expected a non-negative integer in [0, 2^15), got: ${value}`,
61
+ ),
62
+ );
63
+ },
64
+ );
65
+ });
66
+
67
+ describe('isNonNegativeInt16', () => {
68
+ test('correctly identifies non-negative int16 values', () => {
69
+ expect(isNonNegativeInt16(0)).toBe(true);
70
+ expect(isNonNegativeInt16(1)).toBe(true);
71
+ expect(isNonNegativeInt16(1000)).toBe(true);
72
+ expect(isNonNegativeInt16(32767)).toBe(true);
73
+ });
74
+
75
+ test('correctly identifies negative integers', () => {
76
+ expect(isNonNegativeInt16(-1)).toBe(false);
77
+ expect(isNonNegativeInt16(-42)).toBe(false);
78
+ expect(isNonNegativeInt16(-32768)).toBe(false);
79
+ });
80
+
81
+ test('correctly identifies values outside int16 range', () => {
82
+ expect(isNonNegativeInt16(32768)).toBe(false);
83
+ expect(isNonNegativeInt16(65536)).toBe(false);
84
+ });
85
+
86
+ test('correctly identifies non-integers', () => {
87
+ expect(isNonNegativeInt16(Number.NaN)).toBe(false);
88
+ expect(isNonNegativeInt16(Number.POSITIVE_INFINITY)).toBe(false);
89
+ expect(isNonNegativeInt16(Number.NEGATIVE_INFINITY)).toBe(false);
90
+ expect(isNonNegativeInt16(1.2)).toBe(false);
91
+ expect(isNonNegativeInt16(-3.4)).toBe(false);
92
+ });
93
+ });
94
+
95
+ describe('NonNegativeInt16.is', () => {
96
+ test('same as isNonNegativeInt16 function', () => {
97
+ expect(NonNegativeInt16.is(5)).toBe(isNonNegativeInt16(5));
98
+ expect(NonNegativeInt16.is(0)).toBe(isNonNegativeInt16(0));
99
+ expect(NonNegativeInt16.is(-1)).toBe(isNonNegativeInt16(-1));
100
+ });
101
+ });
102
+
103
+ describe('constants', () => {
104
+ test('MIN_VALUE and MAX_VALUE', () => {
105
+ expect(NonNegativeInt16.MIN_VALUE).toBe(0);
106
+ expect(NonNegativeInt16.MAX_VALUE).toBe(32767);
107
+ });
108
+ });
109
+
110
+ describe('mathematical operations', () => {
111
+ const a = asNonNegativeInt16(100);
112
+ const b = asNonNegativeInt16(50);
113
+ const c = asNonNegativeInt16(0);
114
+
115
+ test('min and max', () => {
116
+ expect(NonNegativeInt16.min(a, b)).toBe(50);
117
+ expect(NonNegativeInt16.max(a, b)).toBe(100);
118
+ expect(NonNegativeInt16.min(a, c)).toBe(0);
119
+ expect(NonNegativeInt16.max(a, c)).toBe(100);
120
+ });
121
+
122
+ test('add (with clamping to non-negative int16 range)', () => {
123
+ const result = NonNegativeInt16.add(
124
+ asNonNegativeInt16(32000),
125
+ asNonNegativeInt16(1000),
126
+ );
127
+ expect(result).toBe(32767); // clamped to max
128
+ expect(NonNegativeInt16.add(a, b)).toBe(150);
129
+ });
130
+
131
+ test('sub (never goes below 0)', () => {
132
+ expect(NonNegativeInt16.sub(a, b)).toBe(50);
133
+ expect(NonNegativeInt16.sub(b, a)).toBe(0); // clamped to 0
134
+ expect(NonNegativeInt16.sub(c, a)).toBe(0); // clamped to 0
135
+ });
136
+
137
+ test('mul (with clamping to non-negative int16 range)', () => {
138
+ const result = NonNegativeInt16.mul(
139
+ asNonNegativeInt16(1000),
140
+ asNonNegativeInt16(100),
141
+ );
142
+ expect(result).toBe(32767); // clamped to max
143
+ expect(
144
+ NonNegativeInt16.mul(asNonNegativeInt16(10), asNonNegativeInt16(5)),
145
+ ).toBe(50);
146
+ });
147
+
148
+ test('div (floor division, never goes below 0)', () => {
149
+ expect(NonNegativeInt16.div(a, asPositiveInt16(50))).toBe(2);
150
+ expect(
151
+ NonNegativeInt16.div(asNonNegativeInt16(7), asPositiveInt16(3)),
152
+ ).toBe(2);
153
+ expect(
154
+ NonNegativeInt16.div(asNonNegativeInt16(50), asPositiveInt16(100)),
155
+ ).toBe(0); // floor(50/100) = 0
156
+ });
157
+
158
+ test('pow (with clamping to non-negative int16 range)', () => {
159
+ const result = NonNegativeInt16.pow(
160
+ asNonNegativeInt16(200),
161
+ asNonNegativeInt16(3),
162
+ );
163
+ expect(result).toBe(32767); // clamped to max
164
+ expect(
165
+ NonNegativeInt16.pow(asNonNegativeInt16(2), asNonNegativeInt16(3)),
166
+ ).toBe(8);
167
+ });
168
+ });
169
+
170
+ describe('random', () => {
171
+ test('generates non-negative int16 values within specified range', () => {
172
+ const min = 0;
173
+ const max = 20;
174
+
175
+ for (let i = 0; i < 10; i++) {
176
+ const result = NonNegativeInt16.random(min, max);
177
+ expect(result).toBeGreaterThanOrEqual(min);
178
+ expect(result).toBeLessThanOrEqual(max);
179
+ expect(NonNegativeInt16.is(result)).toBe(true);
180
+ expect(Number.isInteger(result)).toBe(true);
181
+ expect(result).toBeGreaterThanOrEqual(0);
182
+ }
183
+ });
184
+
185
+ test('generates values within NonNegativeInt16 range', () => {
186
+ for (let i = 0; i < 10; i++) {
187
+ const result = NonNegativeInt16.random(0, 30);
188
+ expect(result).toBeGreaterThanOrEqual(0);
189
+ expect(result).toBeLessThanOrEqual(32767);
190
+ }
191
+ });
192
+ });
193
+
194
+ describe('type assertions', () => {
195
+ test('type relationships', () => {
196
+ expectType<NonNegativeInt16, number>('<=');
197
+
198
+ expectTypeOf(asNonNegativeInt16(100)).toExtend<NonNegativeInt16>();
199
+ });
200
+ });
201
+ });
@@ -0,0 +1,187 @@
1
+ import { expectType } from '../../expect-type.mjs';
2
+ import { TsVerifiedInternals } from '../refined-number-utils.mjs';
3
+
4
+ type ElementType = NonNegativeInt32;
5
+
6
+ const typeNameInMessage = 'a non-negative integer in [0, 2^31)';
7
+
8
+ const {
9
+ MIN_VALUE,
10
+ MAX_VALUE,
11
+ min: min_,
12
+ max: max_,
13
+ pow,
14
+ add,
15
+ sub,
16
+ mul,
17
+ div,
18
+ random,
19
+ is,
20
+ castType,
21
+ clamp,
22
+ } = TsVerifiedInternals.RefinedNumberUtils.operatorsForInteger<
23
+ ElementType,
24
+ 0,
25
+ number
26
+ >({
27
+ integerOrSafeInteger: 'SafeInteger',
28
+ MIN_VALUE: 0,
29
+ MAX_VALUE: 2 ** 31 - 1,
30
+ typeNameInMessage,
31
+ } as const);
32
+
33
+ /**
34
+ * Checks if a number is a NonNegativeInt32 (32-bit non-negative signed integer in the range [0, 2^31)).
35
+ * @param value The value to check.
36
+ * @returns `true` if the value is a NonNegativeInt32, `false` otherwise.
37
+ */
38
+ export const isNonNegativeInt32 = is;
39
+
40
+ /**
41
+ * Casts a number to a NonNegativeInt32 type.
42
+ * @param value The value to cast.
43
+ * @returns The value as a NonNegativeInt32 type.
44
+ * @throws {TypeError} If the value is not a non-negative integer in [0, 2^31).
45
+ * @example
46
+ * ```typescript
47
+ * const x = asNonNegativeInt32(1000); // NonNegativeInt32
48
+ * const y = asNonNegativeInt32(0); // NonNegativeInt32
49
+ * // asNonNegativeInt32(-1); // throws TypeError
50
+ * // asNonNegativeInt32(2147483648); // throws TypeError
51
+ * ```
52
+ */
53
+ export const asNonNegativeInt32 = castType;
54
+
55
+ /**
56
+ * Namespace providing type-safe arithmetic operations for 32-bit non-negative integers.
57
+ *
58
+ * All operations automatically clamp results to the valid NonNegativeInt32 range [0, 2147483647].
59
+ * This ensures that all arithmetic maintains the 32-bit non-negative integer constraint,
60
+ * with negative results clamped to 0 and overflow results clamped to MAX_VALUE.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const a = asNonNegativeInt32(2000000000);
65
+ * const b = asNonNegativeInt32(500000000);
66
+ *
67
+ * // Arithmetic operations with automatic clamping
68
+ * const sum = NonNegativeInt32.add(a, b); // NonNegativeInt32 (2147483647 - clamped to MAX_VALUE)
69
+ * const diff = NonNegativeInt32.sub(a, b); // NonNegativeInt32 (1500000000)
70
+ * const reverseDiff = NonNegativeInt32.sub(b, a); // NonNegativeInt32 (0 - clamped to MIN_VALUE)
71
+ * const product = NonNegativeInt32.mul(a, b); // NonNegativeInt32 (2147483647 - clamped due to overflow)
72
+ *
73
+ * // Range operations
74
+ * const clamped = NonNegativeInt32.clamp(-1000); // NonNegativeInt32 (0)
75
+ * const minimum = NonNegativeInt32.min(a, b); // NonNegativeInt32 (500000000)
76
+ * const maximum = NonNegativeInt32.max(a, b); // NonNegativeInt32 (2000000000)
77
+ *
78
+ * // Utility operations
79
+ * const random = NonNegativeInt32.random(); // NonNegativeInt32 (random value in [0, 2147483647])
80
+ * const power = NonNegativeInt32.pow(asNonNegativeInt32(2), asNonNegativeInt32(20)); // NonNegativeInt32 (1048576)
81
+ * ```
82
+ */
83
+ export const NonNegativeInt32 = {
84
+ /**
85
+ * Type guard to check if a value is a NonNegativeInt32.
86
+ * @param value The value to check.
87
+ * @returns `true` if the value is a 32-bit non-negative integer, `false` otherwise.
88
+ */
89
+ is,
90
+
91
+ /**
92
+ * The minimum value for a 32-bit non-negative integer.
93
+ * @readonly
94
+ */
95
+ MIN_VALUE,
96
+
97
+ /**
98
+ * The maximum value for a 32-bit non-negative integer.
99
+ * @readonly
100
+ */
101
+ MAX_VALUE,
102
+
103
+ /**
104
+ * Returns the smaller of two NonNegativeInt32 values.
105
+ * @param a The first NonNegativeInt32.
106
+ * @param b The second NonNegativeInt32.
107
+ * @returns The minimum value as a NonNegativeInt32.
108
+ */
109
+ min: min_,
110
+
111
+ /**
112
+ * Returns the larger of two NonNegativeInt32 values.
113
+ * @param a The first NonNegativeInt32.
114
+ * @param b The second NonNegativeInt32.
115
+ * @returns The maximum value as a NonNegativeInt32.
116
+ */
117
+ max: max_,
118
+
119
+ /**
120
+ * Clamps a number to the NonNegativeInt32 range.
121
+ * @param value The number to clamp.
122
+ * @returns The value clamped to [0, 2147483647] as a NonNegativeInt32.
123
+ */
124
+ clamp,
125
+
126
+ /**
127
+ * Generates a random NonNegativeInt32 value within the valid range.
128
+ * @returns A random NonNegativeInt32 between 0 and 2147483647.
129
+ */
130
+ random,
131
+
132
+ /**
133
+ * Raises a NonNegativeInt32 to the power of another NonNegativeInt32.
134
+ * @param a The base NonNegativeInt32.
135
+ * @param b The exponent NonNegativeInt32.
136
+ * @returns `a ** b` clamped to [0, 2147483647] as a NonNegativeInt32.
137
+ */
138
+ pow,
139
+
140
+ /**
141
+ * Adds two NonNegativeInt32 values.
142
+ * @param a The first NonNegativeInt32.
143
+ * @param b The second NonNegativeInt32.
144
+ * @returns `a + b` clamped to [0, 2147483647] as a NonNegativeInt32.
145
+ */
146
+ add,
147
+
148
+ /**
149
+ * Subtracts one NonNegativeInt32 from another.
150
+ * @param a The minuend NonNegativeInt32.
151
+ * @param b The subtrahend NonNegativeInt32.
152
+ * @returns `a - b` clamped to [0, 2147483647] as a NonNegativeInt32 (minimum 0).
153
+ */
154
+ sub,
155
+
156
+ /**
157
+ * Multiplies two NonNegativeInt32 values.
158
+ * @param a The first NonNegativeInt32.
159
+ * @param b The second NonNegativeInt32.
160
+ * @returns `a * b` clamped to [0, 2147483647] as a NonNegativeInt32.
161
+ */
162
+ mul,
163
+
164
+ /**
165
+ * Divides one NonNegativeInt32 by another using floor division.
166
+ * @param a The dividend NonNegativeInt32.
167
+ * @param b The divisor NonNegativeInt32.
168
+ * @returns `⌊a / b⌋` clamped to [0, 2147483647] as a NonNegativeInt32.
169
+ */
170
+ div,
171
+ } as const;
172
+
173
+ expectType<
174
+ keyof typeof NonNegativeInt32,
175
+ keyof TsVerifiedInternals.RefinedNumberUtils.NumberClass<
176
+ ElementType,
177
+ 'int' | 'non-negative' | 'range'
178
+ >
179
+ >('=');
180
+
181
+ expectType<
182
+ typeof NonNegativeInt32,
183
+ TsVerifiedInternals.RefinedNumberUtils.NumberClass<
184
+ ElementType,
185
+ 'int' | 'non-negative' | 'range'
186
+ >
187
+ >('<=');
@@ -0,0 +1,204 @@
1
+ import { expectType } from '../../expect-type.mjs';
2
+ import {
3
+ asNonNegativeInt32,
4
+ isNonNegativeInt32,
5
+ NonNegativeInt32,
6
+ } from './non-negative-int32.mjs';
7
+ import { asPositiveInt32 } from './positive-int32.mjs';
8
+
9
+ describe('NonNegativeInt32', () => {
10
+ describe('asNonNegativeInt32', () => {
11
+ test('accepts valid non-negative int32 values', () => {
12
+ expect(() => asNonNegativeInt32(0)).not.toThrow();
13
+ expect(() => asNonNegativeInt32(1)).not.toThrow();
14
+ expect(() => asNonNegativeInt32(1000)).not.toThrow();
15
+ expect(() => asNonNegativeInt32(2147483647)).not.toThrow(); // 2^31 - 1
16
+ });
17
+
18
+ test('rejects negative integers', () => {
19
+ expect(() => asNonNegativeInt32(-1)).toThrow(TypeError);
20
+ expect(() => asNonNegativeInt32(-42)).toThrow(TypeError);
21
+ expect(() => asNonNegativeInt32(-2147483648)).toThrow(TypeError);
22
+ });
23
+
24
+ test('rejects values outside int32 range', () => {
25
+ expect(() => asNonNegativeInt32(2147483648)).toThrow(TypeError); // 2^31
26
+ expect(() => asNonNegativeInt32(4294967296)).toThrow(TypeError);
27
+ });
28
+
29
+ test('rejects non-integers', () => {
30
+ expect(() => asNonNegativeInt32(Number.NaN)).toThrow(TypeError);
31
+ expect(() => asNonNegativeInt32(Number.POSITIVE_INFINITY)).toThrow(
32
+ TypeError,
33
+ );
34
+ expect(() => asNonNegativeInt32(Number.NEGATIVE_INFINITY)).toThrow(
35
+ TypeError,
36
+ );
37
+ expect(() => asNonNegativeInt32(1.2)).toThrow(TypeError);
38
+ expect(() => asNonNegativeInt32(-3.4)).toThrow(TypeError);
39
+ });
40
+
41
+ test('returns the same value for valid inputs', () => {
42
+ expect(asNonNegativeInt32(0)).toBe(0);
43
+ expect(asNonNegativeInt32(5)).toBe(5);
44
+ expect(asNonNegativeInt32(2147483647)).toBe(2147483647);
45
+ });
46
+
47
+ test.each([
48
+ { name: 'Number.NaN', value: Number.NaN },
49
+ { name: 'Number.POSITIVE_INFINITY', value: Number.POSITIVE_INFINITY },
50
+ { name: 'Number.NEGATIVE_INFINITY', value: Number.NEGATIVE_INFINITY },
51
+ { name: '1.2', value: 1.2 },
52
+ { name: '-3.4', value: -3.4 },
53
+ { name: '-1', value: -1 },
54
+ { name: '2147483648', value: 2147483648 },
55
+ ] as const)(
56
+ `asNonNegativeInt32($name) should throw a TypeError`,
57
+ ({ value }) => {
58
+ expect(() => asNonNegativeInt32(value)).toThrow(
59
+ new TypeError(
60
+ `Expected a non-negative integer in [0, 2^31), got: ${value}`,
61
+ ),
62
+ );
63
+ },
64
+ );
65
+ });
66
+
67
+ describe('isNonNegativeInt32', () => {
68
+ test('correctly identifies non-negative int32 values', () => {
69
+ expect(isNonNegativeInt32(0)).toBe(true);
70
+ expect(isNonNegativeInt32(1)).toBe(true);
71
+ expect(isNonNegativeInt32(1000)).toBe(true);
72
+ expect(isNonNegativeInt32(2147483647)).toBe(true);
73
+ });
74
+
75
+ test('correctly identifies negative integers', () => {
76
+ expect(isNonNegativeInt32(-1)).toBe(false);
77
+ expect(isNonNegativeInt32(-42)).toBe(false);
78
+ expect(isNonNegativeInt32(-2147483648)).toBe(false);
79
+ });
80
+
81
+ test('correctly identifies values outside int32 range', () => {
82
+ expect(isNonNegativeInt32(2147483648)).toBe(false);
83
+ expect(isNonNegativeInt32(4294967296)).toBe(false);
84
+ });
85
+
86
+ test('correctly identifies non-integers', () => {
87
+ expect(isNonNegativeInt32(Number.NaN)).toBe(false);
88
+ expect(isNonNegativeInt32(Number.POSITIVE_INFINITY)).toBe(false);
89
+ expect(isNonNegativeInt32(Number.NEGATIVE_INFINITY)).toBe(false);
90
+ expect(isNonNegativeInt32(1.2)).toBe(false);
91
+ expect(isNonNegativeInt32(-3.4)).toBe(false);
92
+ });
93
+ });
94
+
95
+ describe('NonNegativeInt32.is', () => {
96
+ test('same as isNonNegativeInt32 function', () => {
97
+ expect(NonNegativeInt32.is(5)).toBe(isNonNegativeInt32(5));
98
+ expect(NonNegativeInt32.is(0)).toBe(isNonNegativeInt32(0));
99
+ expect(NonNegativeInt32.is(-1)).toBe(isNonNegativeInt32(-1));
100
+ });
101
+ });
102
+
103
+ describe('constants', () => {
104
+ test('MIN_VALUE and MAX_VALUE', () => {
105
+ expect(NonNegativeInt32.MIN_VALUE).toBe(0);
106
+ expect(NonNegativeInt32.MAX_VALUE).toBe(2147483647);
107
+ });
108
+ });
109
+
110
+ describe('mathematical operations', () => {
111
+ const a = asNonNegativeInt32(1000000);
112
+ const b = asNonNegativeInt32(500000);
113
+ const c = asNonNegativeInt32(0);
114
+
115
+ test('min and max', () => {
116
+ expect(NonNegativeInt32.min(a, b)).toBe(500000);
117
+ expect(NonNegativeInt32.max(a, b)).toBe(1000000);
118
+ expect(NonNegativeInt32.min(a, c)).toBe(0);
119
+ expect(NonNegativeInt32.max(a, c)).toBe(1000000);
120
+ });
121
+
122
+ test('add (with clamping to non-negative int32 range)', () => {
123
+ const result = NonNegativeInt32.add(
124
+ asNonNegativeInt32(2147483000),
125
+ asNonNegativeInt32(1000),
126
+ );
127
+ expect(result).toBe(2147483647); // clamped to max
128
+ expect(NonNegativeInt32.add(a, b)).toBe(1500000);
129
+ });
130
+
131
+ test('sub (never goes below 0)', () => {
132
+ expect(NonNegativeInt32.sub(a, b)).toBe(500000);
133
+ expect(NonNegativeInt32.sub(b, a)).toBe(0); // clamped to 0
134
+ expect(NonNegativeInt32.sub(c, a)).toBe(0); // clamped to 0
135
+ });
136
+
137
+ test('mul (with clamping to non-negative int32 range)', () => {
138
+ const result = NonNegativeInt32.mul(
139
+ asNonNegativeInt32(100000),
140
+ asNonNegativeInt32(100000),
141
+ );
142
+ expect(result).toBe(2147483647); // clamped to max
143
+ expect(
144
+ NonNegativeInt32.mul(asNonNegativeInt32(1000), asNonNegativeInt32(5)),
145
+ ).toBe(5000);
146
+ });
147
+
148
+ test('div (floor division, never goes below 0)', () => {
149
+ expect(NonNegativeInt32.div(a, asPositiveInt32(500000))).toBe(2);
150
+ expect(
151
+ NonNegativeInt32.div(asNonNegativeInt32(7), asPositiveInt32(3)),
152
+ ).toBe(2);
153
+ expect(
154
+ NonNegativeInt32.div(
155
+ asNonNegativeInt32(500000),
156
+ asPositiveInt32(1000000),
157
+ ),
158
+ ).toBe(0); // floor(500000/1000000) = 0
159
+ });
160
+
161
+ test('pow (with clamping to non-negative int32 range)', () => {
162
+ const result = NonNegativeInt32.pow(
163
+ asNonNegativeInt32(10000),
164
+ asNonNegativeInt32(3),
165
+ );
166
+ expect(result).toBe(2147483647); // clamped to max
167
+ expect(
168
+ NonNegativeInt32.pow(asNonNegativeInt32(2), asNonNegativeInt32(3)),
169
+ ).toBe(8);
170
+ });
171
+ });
172
+
173
+ describe('random', () => {
174
+ test('generates non-negative int32 values within specified range', () => {
175
+ const min = 0;
176
+ const max = 20;
177
+
178
+ for (let i = 0; i < 10; i++) {
179
+ const result = NonNegativeInt32.random(min, max);
180
+ expect(result).toBeGreaterThanOrEqual(min);
181
+ expect(result).toBeLessThanOrEqual(max);
182
+ expect(NonNegativeInt32.is(result)).toBe(true);
183
+ expect(Number.isInteger(result)).toBe(true);
184
+ expect(result).toBeGreaterThanOrEqual(0);
185
+ }
186
+ });
187
+
188
+ test('generates values within NonNegativeInt32 range', () => {
189
+ for (let i = 0; i < 10; i++) {
190
+ const result = NonNegativeInt32.random(0, 30);
191
+ expect(result).toBeGreaterThanOrEqual(0);
192
+ expect(result).toBeLessThanOrEqual(2147483647);
193
+ }
194
+ });
195
+ });
196
+
197
+ describe('type assertions', () => {
198
+ test('type relationships', () => {
199
+ expectType<NonNegativeInt32, number>('<=');
200
+
201
+ expectTypeOf(asNonNegativeInt32(1000000)).toExtend<NonNegativeInt32>();
202
+ });
203
+ });
204
+ });