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