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,187 @@
1
+ import { expectType } from '../../expect-type.mjs';
2
+ import { TsVerifiedInternals } from '../refined-number-utils.mjs';
3
+
4
+ type ElementType = SafeUint;
5
+
6
+ const typeNameInMessage = 'a non-negative safe integer';
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
+ SafeUint
26
+ >({
27
+ integerOrSafeInteger: 'SafeInteger',
28
+ MIN_VALUE: 0,
29
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
30
+ MAX_VALUE: Number.MAX_SAFE_INTEGER as SafeUint,
31
+ typeNameInMessage,
32
+ } as const);
33
+
34
+ /**
35
+ * Checks if a number is a SafeUint.
36
+ * @param value The value to check.
37
+ * @returns `true` if the value is a SafeUint, `false` otherwise.
38
+ */
39
+ export const isSafeUint = is;
40
+
41
+ /**
42
+ * Casts a number to a SafeUint type.
43
+ * @param value The value to cast.
44
+ * @returns The value as a SafeUint type.
45
+ * @throws {TypeError} If the value is not a non-negative safe integer.
46
+ * @example
47
+ * ```typescript
48
+ * const x = asSafeUint(5); // SafeUint
49
+ * const y = asSafeUint(0); // SafeUint
50
+ * // asSafeUint(-1); // throws TypeError
51
+ * // asSafeUint(1.5); // throws TypeError
52
+ * ```
53
+ */
54
+ export const asSafeUint = castType;
55
+
56
+ /**
57
+ * Namespace providing type-safe arithmetic operations for safe unsigned integers.
58
+ *
59
+ * All operations automatically clamp results to the safe unsigned integer range [0, MAX_SAFE_INTEGER].
60
+ * This ensures that all arithmetic maintains both the non-negative constraint and IEEE 754 precision guarantees,
61
+ * preventing precision loss while ensuring results are never negative.
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const a = asSafeUint(9007199254740000); // Near MAX_SAFE_INTEGER
66
+ * const b = asSafeUint(1000);
67
+ *
68
+ * // Arithmetic operations with safe unsigned range clamping
69
+ * const sum = SafeUint.add(a, b); // SafeUint (clamped to MAX_SAFE_INTEGER)
70
+ * const diff = SafeUint.sub(b, a); // SafeUint (0 - clamped to MIN_VALUE)
71
+ * const product = SafeUint.mul(a, b); // SafeUint (clamped to MAX_SAFE_INTEGER)
72
+ *
73
+ * // Range operations
74
+ * const clamped = SafeUint.clamp(-100); // SafeUint (0)
75
+ * const minimum = SafeUint.min(a, b); // SafeUint (1000)
76
+ * const maximum = SafeUint.max(a, b); // SafeUint (a)
77
+ *
78
+ * // Utility operations
79
+ * const random = SafeUint.random(); // SafeUint (random safe unsigned integer)
80
+ * const power = SafeUint.pow(asSafeUint(2), asSafeUint(20)); // SafeUint (1048576)
81
+ * ```
82
+ */
83
+ export const SafeUint = {
84
+ /**
85
+ * Type guard to check if a value is a SafeUint.
86
+ * @param value The value to check.
87
+ * @returns `true` if the value is a non-negative safe integer, `false` otherwise.
88
+ */
89
+ is,
90
+
91
+ /**
92
+ * The minimum value for a safe unsigned integer.
93
+ * @readonly
94
+ */
95
+ MIN_VALUE,
96
+
97
+ /**
98
+ * The maximum safe integer value (2^53 - 1).
99
+ * @readonly
100
+ */
101
+ MAX_VALUE,
102
+
103
+ /**
104
+ * Returns the smaller of two SafeUint values.
105
+ * @param a The first SafeUint.
106
+ * @param b The second SafeUint.
107
+ * @returns The minimum value as a SafeUint.
108
+ */
109
+ min: min_,
110
+
111
+ /**
112
+ * Returns the larger of two SafeUint values.
113
+ * @param a The first SafeUint.
114
+ * @param b The second SafeUint.
115
+ * @returns The maximum value as a SafeUint.
116
+ */
117
+ max: max_,
118
+
119
+ /**
120
+ * Clamps a number to the safe unsigned integer range.
121
+ * @param value The number to clamp.
122
+ * @returns The value clamped to [0, MAX_SAFE_INTEGER] as a SafeUint.
123
+ */
124
+ clamp,
125
+
126
+ /**
127
+ * Generates a random SafeUint value within the valid range.
128
+ * @returns A random SafeUint between 0 and MAX_SAFE_INTEGER.
129
+ */
130
+ random,
131
+
132
+ /**
133
+ * Raises a SafeUint to the power of another SafeUint.
134
+ * @param a The base SafeUint.
135
+ * @param b The exponent SafeUint.
136
+ * @returns `a ** b` clamped to [0, MAX_SAFE_INTEGER] as a SafeUint.
137
+ */
138
+ pow,
139
+
140
+ /**
141
+ * Adds two SafeUint values.
142
+ * @param a The first SafeUint.
143
+ * @param b The second SafeUint.
144
+ * @returns `a + b` clamped to [0, MAX_SAFE_INTEGER] as a SafeUint.
145
+ */
146
+ add,
147
+
148
+ /**
149
+ * Subtracts one SafeUint from another.
150
+ * @param a The minuend SafeUint.
151
+ * @param b The subtrahend SafeUint.
152
+ * @returns `a - b` clamped to [0, MAX_SAFE_INTEGER] as a SafeUint (minimum 0).
153
+ */
154
+ sub,
155
+
156
+ /**
157
+ * Multiplies two SafeUint values.
158
+ * @param a The first SafeUint.
159
+ * @param b The second SafeUint.
160
+ * @returns `a * b` clamped to [0, MAX_SAFE_INTEGER] as a SafeUint.
161
+ */
162
+ mul,
163
+
164
+ /**
165
+ * Divides one SafeUint by another using floor division.
166
+ * @param a The dividend SafeUint.
167
+ * @param b The divisor SafeUint.
168
+ * @returns `⌊a / b⌋` clamped to [0, MAX_SAFE_INTEGER] as a SafeUint.
169
+ */
170
+ div,
171
+ } as const;
172
+
173
+ expectType<
174
+ keyof typeof SafeUint,
175
+ keyof TsVerifiedInternals.RefinedNumberUtils.NumberClass<
176
+ ElementType,
177
+ 'int' | 'non-negative' | 'range'
178
+ >
179
+ >('=');
180
+
181
+ expectType<
182
+ typeof SafeUint,
183
+ TsVerifiedInternals.RefinedNumberUtils.NumberClass<
184
+ ElementType,
185
+ 'int' | 'non-negative' | 'range'
186
+ >
187
+ >('<=');
@@ -0,0 +1,176 @@
1
+ import { expectType } from '../../expect-type.mjs';
2
+ import { asSafeUint, isSafeUint, SafeUint } from './safe-uint.mjs';
3
+
4
+ describe('SafeUint', () => {
5
+ describe('asSafeUint', () => {
6
+ test('accepts valid safe unsigned integers', () => {
7
+ expect(() => asSafeUint(0)).not.toThrow();
8
+ expect(() => asSafeUint(1)).not.toThrow();
9
+ expect(() => asSafeUint(42)).not.toThrow();
10
+ expect(() => asSafeUint(100)).not.toThrow();
11
+ expect(() => asSafeUint(Number.MAX_SAFE_INTEGER)).not.toThrow();
12
+ });
13
+
14
+ test('rejects negative numbers', () => {
15
+ expect(() => asSafeUint(-1)).toThrow(TypeError);
16
+ expect(() => asSafeUint(-42)).toThrow(TypeError);
17
+ expect(() => asSafeUint(Number.MIN_SAFE_INTEGER)).toThrow(TypeError);
18
+ });
19
+
20
+ test('rejects values outside safe integer range', () => {
21
+ expect(() => asSafeUint(Number.MAX_SAFE_INTEGER + 1)).toThrow(TypeError);
22
+ expect(() => asSafeUint(Number.MAX_VALUE)).toThrow(TypeError);
23
+ });
24
+
25
+ test('rejects non-integers', () => {
26
+ expect(() => asSafeUint(Number.NaN)).toThrow(TypeError);
27
+ expect(() => asSafeUint(Number.POSITIVE_INFINITY)).toThrow(TypeError);
28
+ expect(() => asSafeUint(Number.NEGATIVE_INFINITY)).toThrow(TypeError);
29
+ expect(() => asSafeUint(1.2)).toThrow(TypeError);
30
+ expect(() => asSafeUint(-3.4)).toThrow(TypeError);
31
+ });
32
+
33
+ test('returns the same value for valid inputs', () => {
34
+ expect(asSafeUint(5)).toBe(5);
35
+ expect(asSafeUint(0)).toBe(0);
36
+ expect(asSafeUint(10)).toBe(10);
37
+ expect(asSafeUint(Number.MAX_SAFE_INTEGER)).toBe(Number.MAX_SAFE_INTEGER);
38
+ });
39
+
40
+ test.each([
41
+ { name: 'Number.NaN', value: Number.NaN },
42
+ { name: 'Number.POSITIVE_INFINITY', value: Number.POSITIVE_INFINITY },
43
+ { name: 'Number.NEGATIVE_INFINITY', value: Number.NEGATIVE_INFINITY },
44
+ { name: '1.2', value: 1.2 },
45
+ { name: '-3.4', value: -3.4 },
46
+ { name: '-1', value: -1 },
47
+ ] as const)(`asSafeUint($name) should throw a TypeError`, ({ value }) => {
48
+ expect(() => asSafeUint(value)).toThrow(
49
+ new TypeError(`Expected a non-negative safe integer, got: ${value}`),
50
+ );
51
+ });
52
+ });
53
+
54
+ describe('isSafeUint', () => {
55
+ test('correctly identifies safe unsigned integers', () => {
56
+ expect(isSafeUint(0)).toBe(true);
57
+ expect(isSafeUint(1)).toBe(true);
58
+ expect(isSafeUint(42)).toBe(true);
59
+ expect(isSafeUint(100)).toBe(true);
60
+ expect(isSafeUint(Number.MAX_SAFE_INTEGER)).toBe(true);
61
+ });
62
+
63
+ test('correctly identifies negative numbers', () => {
64
+ expect(isSafeUint(-1)).toBe(false);
65
+ expect(isSafeUint(-42)).toBe(false);
66
+ expect(isSafeUint(Number.MIN_SAFE_INTEGER)).toBe(false);
67
+ });
68
+
69
+ test('correctly identifies values outside safe integer range', () => {
70
+ expect(isSafeUint(Number.MAX_SAFE_INTEGER + 1)).toBe(false);
71
+ expect(isSafeUint(Number.MAX_VALUE)).toBe(false);
72
+ });
73
+
74
+ test('correctly identifies non-integers', () => {
75
+ expect(isSafeUint(Number.NaN)).toBe(false);
76
+ expect(isSafeUint(Number.POSITIVE_INFINITY)).toBe(false);
77
+ expect(isSafeUint(Number.NEGATIVE_INFINITY)).toBe(false);
78
+ expect(isSafeUint(1.2)).toBe(false);
79
+ expect(isSafeUint(-3.4)).toBe(false);
80
+ });
81
+ });
82
+
83
+ describe('SafeUint.is', () => {
84
+ test('same as isSafeUint function', () => {
85
+ expect(SafeUint.is(5)).toBe(isSafeUint(5));
86
+ expect(SafeUint.is(-1)).toBe(isSafeUint(-1));
87
+ expect(SafeUint.is(0)).toBe(isSafeUint(0));
88
+ });
89
+ });
90
+
91
+ describe('constants', () => {
92
+ test('MIN_VALUE and MAX_VALUE', () => {
93
+ expect(SafeUint.MIN_VALUE).toBe(0);
94
+ expect(SafeUint.MAX_VALUE).toBe(Number.MAX_SAFE_INTEGER);
95
+ });
96
+ });
97
+
98
+ describe('mathematical operations', () => {
99
+ const a = asSafeUint(5);
100
+ const b = asSafeUint(2);
101
+ const c = asSafeUint(0);
102
+
103
+ test('min and max', () => {
104
+ expect(SafeUint.min(a, b)).toBe(2);
105
+ expect(SafeUint.max(a, b)).toBe(5);
106
+ expect(SafeUint.min(a, c)).toBe(0);
107
+ expect(SafeUint.max(a, c)).toBe(5);
108
+ });
109
+
110
+ test('add (clamped to safe uint range)', () => {
111
+ const largeValue = asSafeUint(Number.MAX_SAFE_INTEGER - 1);
112
+ const result = SafeUint.add(largeValue, asSafeUint(10));
113
+ expect(result).toBe(Number.MAX_SAFE_INTEGER); // clamped to max
114
+ expect(SafeUint.add(a, b)).toBe(7);
115
+ });
116
+
117
+ test('sub (never goes below 0)', () => {
118
+ expect(SafeUint.sub(a, b)).toBe(3);
119
+ expect(SafeUint.sub(b, a)).toBe(0); // clamped to 0
120
+ expect(SafeUint.sub(c, a)).toBe(0); // clamped to 0
121
+ });
122
+
123
+ test('mul (clamped to safe uint range)', () => {
124
+ const largeValue = asSafeUint(
125
+ Math.floor(Math.sqrt(Number.MAX_SAFE_INTEGER)),
126
+ );
127
+ const result = SafeUint.mul(largeValue, largeValue);
128
+ expect(result).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER);
129
+ expect(SafeUint.mul(asSafeUint(10), asSafeUint(5))).toBe(50);
130
+ });
131
+
132
+ test('div (floor division, never goes below 0)', () => {
133
+ expect(SafeUint.div(a, b)).toBe(2);
134
+ expect(SafeUint.div(asSafeUint(7), asSafeUint(3))).toBe(2);
135
+ expect(SafeUint.div(b, a)).toBe(0); // floor(2/5) = 0
136
+ });
137
+
138
+ test('pow (clamped to safe uint range)', () => {
139
+ const result = SafeUint.pow(asSafeUint(1000), asSafeUint(10));
140
+ expect(result).toBe(Number.MAX_SAFE_INTEGER); // clamped to max
141
+ expect(SafeUint.pow(asSafeUint(2), asSafeUint(3))).toBe(8);
142
+ });
143
+ });
144
+
145
+ describe('random', () => {
146
+ test('generates safe unsigned integers within specified range', () => {
147
+ const min = 0;
148
+ const max = 20;
149
+
150
+ for (let i = 0; i < 10; i++) {
151
+ const result = SafeUint.random(min, max);
152
+ expect(result).toBeGreaterThanOrEqual(min);
153
+ expect(result).toBeLessThanOrEqual(max);
154
+ expect(SafeUint.is(result)).toBe(true);
155
+ expect(Number.isInteger(result)).toBe(true);
156
+ expect(result).toBeGreaterThanOrEqual(0);
157
+ }
158
+ });
159
+
160
+ test('generates values within safe uint range', () => {
161
+ for (let i = 0; i < 10; i++) {
162
+ const result = SafeUint.random(0, 30);
163
+ expect(result).toBeGreaterThanOrEqual(0);
164
+ expect(result).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER);
165
+ }
166
+ });
167
+ });
168
+
169
+ describe('type assertions', () => {
170
+ test('type relationships', () => {
171
+ expectType<SafeUint, number>('<=');
172
+
173
+ expectTypeOf(asSafeUint(5)).toExtend<SafeUint>();
174
+ });
175
+ });
176
+ });
@@ -0,0 +1,179 @@
1
+ import { expectType } from '../../expect-type.mjs';
2
+ import { TsVerifiedInternals } from '../refined-number-utils.mjs';
3
+
4
+ type ElementType = Uint;
5
+
6
+ const typeNameInMessage = 'a non-negative integer';
7
+
8
+ const {
9
+ MIN_VALUE,
10
+ min: min_,
11
+ max: max_,
12
+ pow,
13
+ add,
14
+ sub,
15
+ mul,
16
+ div,
17
+ random,
18
+ is,
19
+ castType,
20
+ clamp,
21
+ } = TsVerifiedInternals.RefinedNumberUtils.operatorsForInteger<
22
+ ElementType,
23
+ 0,
24
+ undefined
25
+ >({
26
+ integerOrSafeInteger: 'Integer',
27
+ MIN_VALUE: 0,
28
+ MAX_VALUE: undefined,
29
+ typeNameInMessage,
30
+ } as const);
31
+
32
+ /**
33
+ * Checks if a number is a Uint.
34
+ * @param value The value to check.
35
+ * @returns `true` if the value is a Uint, `false` otherwise.
36
+ */
37
+ export const isUint = is;
38
+
39
+ /**
40
+ * Casts a number to a Uint type.
41
+ * @param value The value to cast.
42
+ * @returns The value as a Uint type.
43
+ * @throws {TypeError} If the value is not a non-negative integer.
44
+ * @example
45
+ * ```typescript
46
+ * const x = asUint(5); // Uint
47
+ * const y = asUint(0); // Uint
48
+ * // asUint(-1); // throws TypeError
49
+ * // asUint(1.5); // throws TypeError
50
+ * ```
51
+ */
52
+ export const asUint = castType;
53
+
54
+ /**
55
+ * Namespace providing type-safe arithmetic operations for unsigned integers.
56
+ *
57
+ * All operations maintain the non-negative constraint by clamping negative results to 0.
58
+ * This ensures that all arithmetic preserves the unsigned integer property.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const a = asUint(100);
63
+ * const b = asUint(150);
64
+ *
65
+ * // Arithmetic operations with non-negative clamping
66
+ * const sum = Uint.add(a, b); // Uint (250)
67
+ * const diff = Uint.sub(a, b); // Uint (0 - clamped to MIN_VALUE)
68
+ * const product = Uint.mul(a, b); // Uint (15000)
69
+ * const quotient = Uint.div(b, a); // Uint (1)
70
+ *
71
+ * // Range operations
72
+ * const clamped = Uint.clamp(-50); // Uint (0)
73
+ * const minimum = Uint.min(a, b); // Uint (100)
74
+ * const maximum = Uint.max(a, b); // Uint (150)
75
+ *
76
+ * // Utility operations
77
+ * const random = Uint.random(); // Uint (random non-negative integer)
78
+ * const power = Uint.pow(asUint(2), asUint(8)); // Uint (256)
79
+ * ```
80
+ */
81
+ export const Uint = {
82
+ /**
83
+ * Type guard to check if a value is a Uint.
84
+ * @param value The value to check.
85
+ * @returns `true` if the value is a non-negative integer, `false` otherwise.
86
+ */
87
+ is,
88
+
89
+ /**
90
+ * The minimum value for an unsigned integer.
91
+ * @readonly
92
+ */
93
+ MIN_VALUE,
94
+
95
+ /**
96
+ * Returns the smaller of two Uint values.
97
+ * @param a The first Uint.
98
+ * @param b The second Uint.
99
+ * @returns The minimum value as a Uint.
100
+ */
101
+ min: min_,
102
+
103
+ /**
104
+ * Returns the larger of two Uint values.
105
+ * @param a The first Uint.
106
+ * @param b The second Uint.
107
+ * @returns The maximum value as a Uint.
108
+ */
109
+ max: max_,
110
+
111
+ /**
112
+ * Clamps a number to the Uint range (non-negative).
113
+ * @param value The number to clamp.
114
+ * @returns The value clamped to [0, +∞) as a Uint.
115
+ */
116
+ clamp,
117
+
118
+ /**
119
+ * Generates a random Uint value.
120
+ * @returns A random non-negative integer as a Uint.
121
+ */
122
+ random,
123
+
124
+ /**
125
+ * Raises a Uint to the power of another Uint.
126
+ * @param a The base Uint.
127
+ * @param b The exponent Uint.
128
+ * @returns `a ** b` clamped to [0, +∞) as a Uint.
129
+ */
130
+ pow,
131
+
132
+ /**
133
+ * Adds two Uint values.
134
+ * @param a The first Uint.
135
+ * @param b The second Uint.
136
+ * @returns `a + b` clamped to [0, +∞) as a Uint.
137
+ */
138
+ add,
139
+
140
+ /**
141
+ * Subtracts one Uint from another.
142
+ * @param a The minuend Uint.
143
+ * @param b The subtrahend Uint.
144
+ * @returns `a - b` clamped to [0, +∞) as a Uint (minimum 0).
145
+ */
146
+ sub,
147
+
148
+ /**
149
+ * Multiplies two Uint values.
150
+ * @param a The first Uint.
151
+ * @param b The second Uint.
152
+ * @returns `a * b` clamped to [0, +∞) as a Uint.
153
+ */
154
+ mul,
155
+
156
+ /**
157
+ * Divides one Uint by another using floor division.
158
+ * @param a The dividend Uint.
159
+ * @param b The divisor Uint.
160
+ * @returns `⌊a / b⌋` clamped to [0, +∞) as a Uint.
161
+ */
162
+ div,
163
+ } as const;
164
+
165
+ expectType<
166
+ keyof typeof Uint,
167
+ keyof TsVerifiedInternals.RefinedNumberUtils.NumberClass<
168
+ ElementType,
169
+ 'int' | 'non-negative'
170
+ >
171
+ >('=');
172
+
173
+ expectType<
174
+ typeof Uint,
175
+ TsVerifiedInternals.RefinedNumberUtils.NumberClass<
176
+ ElementType,
177
+ 'int' | 'non-negative'
178
+ >
179
+ >('<=');
@@ -0,0 +1,158 @@
1
+ import { expectType } from '../../expect-type.mjs';
2
+ import { asUint, isUint, Uint } from './uint.mjs';
3
+
4
+ describe('Uint', () => {
5
+ describe('asUint', () => {
6
+ test('accepts valid unsigned integers', () => {
7
+ expect(() => asUint(0)).not.toThrow();
8
+ expect(() => asUint(1)).not.toThrow();
9
+ expect(() => asUint(42)).not.toThrow();
10
+ expect(() => asUint(100)).not.toThrow();
11
+ expect(() => asUint(Number.MAX_SAFE_INTEGER)).not.toThrow();
12
+ });
13
+
14
+ test('rejects negative integers', () => {
15
+ expect(() => asUint(-1)).toThrow(TypeError);
16
+ expect(() => asUint(-42)).toThrow(TypeError);
17
+ expect(() => asUint(Number.MIN_SAFE_INTEGER)).toThrow(TypeError);
18
+ });
19
+
20
+ test('rejects non-integers', () => {
21
+ expect(() => asUint(Number.NaN)).toThrow(TypeError);
22
+ expect(() => asUint(Number.POSITIVE_INFINITY)).toThrow(TypeError);
23
+ expect(() => asUint(Number.NEGATIVE_INFINITY)).toThrow(TypeError);
24
+ expect(() => asUint(1.2)).toThrow(TypeError);
25
+ expect(() => asUint(-3.4)).toThrow(TypeError);
26
+ });
27
+
28
+ test('returns the same value for valid inputs', () => {
29
+ expect(asUint(5)).toBe(5);
30
+ expect(asUint(0)).toBe(0);
31
+ expect(asUint(10)).toBe(10);
32
+ });
33
+
34
+ test.each([
35
+ { name: 'Number.NaN', value: Number.NaN },
36
+ { name: 'Number.POSITIVE_INFINITY', value: Number.POSITIVE_INFINITY },
37
+ { name: 'Number.NEGATIVE_INFINITY', value: Number.NEGATIVE_INFINITY },
38
+ { name: '1.2', value: 1.2 },
39
+ { name: '-3.4', value: -3.4 },
40
+ { name: '-1', value: -1 },
41
+ ] as const)(`asUint($name) should throw a TypeError`, ({ value }) => {
42
+ expect(() => asUint(value)).toThrow(
43
+ new TypeError(`Expected a non-negative integer, got: ${value}`),
44
+ );
45
+ });
46
+ });
47
+
48
+ describe('isUint', () => {
49
+ test('correctly identifies unsigned integers', () => {
50
+ expect(isUint(0)).toBe(true);
51
+ expect(isUint(1)).toBe(true);
52
+ expect(isUint(42)).toBe(true);
53
+ expect(isUint(100)).toBe(true);
54
+ expect(isUint(Number.MAX_SAFE_INTEGER)).toBe(true);
55
+ });
56
+
57
+ test('correctly identifies negative integers', () => {
58
+ expect(isUint(-1)).toBe(false);
59
+ expect(isUint(-42)).toBe(false);
60
+ expect(isUint(Number.MIN_SAFE_INTEGER)).toBe(false);
61
+ });
62
+
63
+ test('correctly identifies non-integers', () => {
64
+ expect(isUint(Number.NaN)).toBe(false);
65
+ expect(isUint(Number.POSITIVE_INFINITY)).toBe(false);
66
+ expect(isUint(Number.NEGATIVE_INFINITY)).toBe(false);
67
+ expect(isUint(1.2)).toBe(false);
68
+ expect(isUint(-3.4)).toBe(false);
69
+ });
70
+ });
71
+
72
+ describe('Uint.is', () => {
73
+ test('same as isUint function', () => {
74
+ expect(Uint.is(5)).toBe(isUint(5));
75
+ expect(Uint.is(-1)).toBe(isUint(-1));
76
+ expect(Uint.is(0)).toBe(isUint(0));
77
+ });
78
+ });
79
+
80
+ describe('constants', () => {
81
+ test('MIN_VALUE', () => {
82
+ expect(Uint.MIN_VALUE).toBe(0);
83
+ });
84
+ });
85
+
86
+ describe('mathematical operations', () => {
87
+ const a = asUint(5);
88
+ const b = asUint(2);
89
+ const c = asUint(0);
90
+
91
+ test('min and max', () => {
92
+ expect(Uint.min(a, b)).toBe(2);
93
+ expect(Uint.max(a, b)).toBe(5);
94
+ expect(Uint.min(a, c)).toBe(0);
95
+ expect(Uint.max(a, c)).toBe(5);
96
+ });
97
+
98
+ test('add (never goes below 0)', () => {
99
+ expect(Uint.add(a, b)).toBe(7);
100
+ expect(Uint.add(a, c)).toBe(5);
101
+ });
102
+
103
+ test('sub (never goes below 0)', () => {
104
+ expect(Uint.sub(a, b)).toBe(3);
105
+ expect(Uint.sub(b, a)).toBe(0); // clamped to 0
106
+ expect(Uint.sub(c, a)).toBe(0); // clamped to 0
107
+ });
108
+
109
+ test('mul (never goes below 0)', () => {
110
+ expect(Uint.mul(a, b)).toBe(10);
111
+ expect(Uint.mul(a, c)).toBe(0);
112
+ });
113
+
114
+ test('div (floor division, never goes below 0)', () => {
115
+ expect(Uint.div(a, b)).toBe(2);
116
+ expect(Uint.div(asUint(7), asUint(3))).toBe(2);
117
+ expect(Uint.div(b, a)).toBe(0); // floor(2/5) = 0
118
+ });
119
+
120
+ test('pow (never goes below 0)', () => {
121
+ expect(Uint.pow(asUint(2), asUint(3))).toBe(8);
122
+ expect(Uint.pow(asUint(3), asUint(2))).toBe(9);
123
+ expect(Uint.pow(asUint(5), asUint(0))).toBe(1);
124
+ });
125
+ });
126
+
127
+ describe('random', () => {
128
+ test('generates unsigned integers within specified range', () => {
129
+ const min = 0;
130
+ const max = 20;
131
+
132
+ for (let i = 0; i < 10; i++) {
133
+ const result = Uint.random(min, max);
134
+ expect(result).toBeGreaterThanOrEqual(min);
135
+ expect(result).toBeLessThanOrEqual(max);
136
+ expect(Uint.is(result)).toBe(true);
137
+ expect(Number.isInteger(result)).toBe(true);
138
+ expect(result).toBeGreaterThanOrEqual(0);
139
+ }
140
+ });
141
+
142
+ test('generates values starting from 0', () => {
143
+ for (let i = 0; i < 10; i++) {
144
+ const result = Uint.random(0, 30);
145
+ expect(result).toBeGreaterThanOrEqual(0);
146
+ expect(result).toBeLessThanOrEqual(30);
147
+ }
148
+ });
149
+ });
150
+
151
+ describe('type assertions', () => {
152
+ test('type relationships', () => {
153
+ expectType<Uint, number>('<=');
154
+
155
+ expectTypeOf(asUint(5)).toExtend<Uint>();
156
+ });
157
+ });
158
+ });