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