ts-data-forge 6.13.1 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/array/impl/array-utils-creation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-creation.mjs +1 -1
- package/dist/array/impl/array-utils-creation.mjs.map +1 -1
- package/dist/array/impl/array-utils-iterators.mjs +7 -1
- package/dist/array/impl/array-utils-iterators.mjs.map +1 -1
- package/dist/array/impl/array-utils-modification.mjs +6 -0
- package/dist/array/impl/array-utils-modification.mjs.map +1 -1
- package/dist/array/impl/array-utils-reducing-value.d.mts +1 -1
- package/dist/array/impl/array-utils-reducing-value.d.mts.map +1 -1
- package/dist/array/impl/array-utils-reducing-value.mjs +6 -1
- package/dist/array/impl/array-utils-reducing-value.mjs.map +1 -1
- package/dist/array/impl/array-utils-search.d.mts +0 -2
- package/dist/array/impl/array-utils-search.d.mts.map +1 -1
- package/dist/array/impl/array-utils-search.mjs +6 -0
- package/dist/array/impl/array-utils-search.mjs.map +1 -1
- package/dist/array/impl/array-utils-set-op.d.mts.map +1 -1
- package/dist/array/impl/array-utils-set-op.mjs +0 -1
- package/dist/array/impl/array-utils-set-op.mjs.map +1 -1
- package/dist/array/impl/array-utils-slice-clamped.mjs +6 -0
- package/dist/array/impl/array-utils-slice-clamped.mjs.map +1 -1
- package/dist/array/impl/array-utils-slicing.mjs +6 -0
- package/dist/array/impl/array-utils-slicing.mjs.map +1 -1
- package/dist/array/impl/array-utils-transformation.d.mts +1 -1
- package/dist/array/impl/array-utils-transformation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-transformation.mjs +8 -4
- package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
- package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-validation.mjs +9 -9
- package/dist/array/impl/array-utils-validation.mjs.map +1 -1
- package/dist/collections/imap-mapped.d.mts +622 -3
- package/dist/collections/imap-mapped.d.mts.map +1 -1
- package/dist/collections/imap-mapped.mjs +117 -18
- package/dist/collections/imap-mapped.mjs.map +1 -1
- package/dist/collections/imap.d.mts +3 -3
- package/dist/collections/imap.d.mts.map +1 -1
- package/dist/collections/imap.mjs +12 -5
- package/dist/collections/imap.mjs.map +1 -1
- package/dist/collections/iset-mapped.d.mts +4 -4
- package/dist/collections/iset-mapped.d.mts.map +1 -1
- package/dist/collections/iset-mapped.mjs +14 -4
- package/dist/collections/iset-mapped.mjs.map +1 -1
- package/dist/collections/iset.d.mts +2 -2
- package/dist/collections/iset.d.mts.map +1 -1
- package/dist/collections/iset.mjs +11 -2
- package/dist/collections/iset.mjs.map +1 -1
- package/dist/collections/queue.mjs +26 -20
- package/dist/collections/queue.mjs.map +1 -1
- package/dist/collections/stack.mjs +23 -17
- package/dist/collections/stack.mjs.map +1 -1
- package/dist/entry-point.mjs +6 -0
- package/dist/entry-point.mjs.map +1 -1
- package/dist/functional/optional/impl/optional-flat-map.d.mts +2 -2
- package/dist/functional/pipe.d.mts.map +1 -1
- package/dist/functional/pipe.mjs.map +1 -1
- package/dist/functional/result/impl/result-flat-map.d.mts +2 -2
- package/dist/functional/ternary-result/impl/ternary-result-flat-map.d.mts +5 -3
- package/dist/functional/ternary-result/impl/ternary-result-flat-map.d.mts.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-flat-map.mjs.map +1 -1
- package/dist/guard/has-key.d.mts.map +1 -1
- package/dist/guard/has-key.mjs +1 -3
- package/dist/guard/has-key.mjs.map +1 -1
- package/dist/guard/is-non-null-object.d.mts.map +1 -1
- package/dist/guard/is-non-null-object.mjs +1 -3
- package/dist/guard/is-non-null-object.mjs.map +1 -1
- package/dist/guard/key-is-in.d.mts.map +1 -1
- package/dist/guard/key-is-in.mjs +1 -3
- package/dist/guard/key-is-in.mjs.map +1 -1
- package/dist/index.mjs +6 -0
- package/dist/index.mjs.map +1 -1
- package/dist/iterator/range.mjs +6 -0
- package/dist/iterator/range.mjs.map +1 -1
- package/dist/json/json.mjs +11 -7
- package/dist/json/json.mjs.map +1 -1
- package/dist/number/branded-types/finite-number.d.mts +1 -1
- package/dist/number/branded-types/index.d.mts +6 -0
- package/dist/number/branded-types/index.d.mts.map +1 -1
- package/dist/number/branded-types/index.mjs +6 -0
- package/dist/number/branded-types/index.mjs.map +1 -1
- package/dist/number/branded-types/int.d.mts +1 -2
- package/dist/number/branded-types/int.d.mts.map +1 -1
- package/dist/number/branded-types/int16.d.mts +2 -3
- package/dist/number/branded-types/int16.d.mts.map +1 -1
- package/dist/number/branded-types/int32.d.mts +2 -3
- package/dist/number/branded-types/int32.d.mts.map +1 -1
- package/dist/number/branded-types/negative-finite-number.d.mts +143 -0
- package/dist/number/branded-types/negative-finite-number.d.mts.map +1 -0
- package/dist/number/branded-types/negative-finite-number.mjs +156 -0
- package/dist/number/branded-types/negative-finite-number.mjs.map +1 -0
- package/dist/number/branded-types/negative-int.d.mts +120 -0
- package/dist/number/branded-types/negative-int.d.mts.map +1 -0
- package/dist/number/branded-types/negative-int.mjs +125 -0
- package/dist/number/branded-types/negative-int.mjs.map +1 -0
- package/dist/number/branded-types/negative-safe-int.d.mts +132 -0
- package/dist/number/branded-types/negative-safe-int.d.mts.map +1 -0
- package/dist/number/branded-types/negative-safe-int.mjs +136 -0
- package/dist/number/branded-types/negative-safe-int.mjs.map +1 -0
- package/dist/number/branded-types/non-negative-finite-number.d.mts +1 -1
- package/dist/number/branded-types/non-negative-int16.d.mts +1 -1
- package/dist/number/branded-types/non-negative-int32.d.mts +1 -1
- package/dist/number/branded-types/non-positive-finite-number.d.mts +130 -0
- package/dist/number/branded-types/non-positive-finite-number.d.mts.map +1 -0
- package/dist/number/branded-types/non-positive-finite-number.mjs +134 -0
- package/dist/number/branded-types/non-positive-finite-number.mjs.map +1 -0
- package/dist/number/branded-types/non-positive-int.d.mts +224 -0
- package/dist/number/branded-types/non-positive-int.d.mts.map +1 -0
- package/dist/number/branded-types/non-positive-int.mjs +222 -0
- package/dist/number/branded-types/non-positive-int.mjs.map +1 -0
- package/dist/number/branded-types/non-positive-safe-int.d.mts +119 -0
- package/dist/number/branded-types/non-positive-safe-int.d.mts.map +1 -0
- package/dist/number/branded-types/non-positive-safe-int.mjs +116 -0
- package/dist/number/branded-types/non-positive-safe-int.mjs.map +1 -0
- package/dist/number/branded-types/non-zero-finite-number.d.mts +5 -17
- package/dist/number/branded-types/non-zero-finite-number.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-finite-number.mjs +5 -17
- package/dist/number/branded-types/non-zero-finite-number.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-int.d.mts +5 -33
- package/dist/number/branded-types/non-zero-int.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-int.mjs +5 -25
- package/dist/number/branded-types/non-zero-int.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-int16.d.mts +6 -43
- package/dist/number/branded-types/non-zero-int16.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-int16.mjs +5 -25
- package/dist/number/branded-types/non-zero-int16.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-int32.d.mts +6 -39
- package/dist/number/branded-types/non-zero-int32.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-int32.mjs +5 -25
- package/dist/number/branded-types/non-zero-int32.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-safe-int.d.mts +5 -37
- package/dist/number/branded-types/non-zero-safe-int.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-safe-int.mjs +7 -30
- package/dist/number/branded-types/non-zero-safe-int.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-uint16.d.mts +6 -33
- package/dist/number/branded-types/non-zero-uint16.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-uint16.mjs +5 -17
- package/dist/number/branded-types/non-zero-uint16.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-uint32.d.mts +6 -31
- package/dist/number/branded-types/non-zero-uint32.d.mts.map +1 -1
- package/dist/number/branded-types/non-zero-uint32.mjs +5 -17
- package/dist/number/branded-types/non-zero-uint32.mjs.map +1 -1
- package/dist/number/branded-types/positive-finite-number.d.mts +4 -2
- package/dist/number/branded-types/positive-finite-number.d.mts.map +1 -1
- package/dist/number/branded-types/positive-int.d.mts +4 -2
- package/dist/number/branded-types/positive-int.d.mts.map +1 -1
- package/dist/number/branded-types/positive-int16.d.mts +4 -2
- package/dist/number/branded-types/positive-int16.d.mts.map +1 -1
- package/dist/number/branded-types/positive-int32.d.mts +4 -2
- package/dist/number/branded-types/positive-int32.d.mts.map +1 -1
- package/dist/number/branded-types/positive-safe-int.d.mts +4 -2
- package/dist/number/branded-types/positive-safe-int.d.mts.map +1 -1
- package/dist/number/branded-types/positive-safe-int.mjs +1 -1
- package/dist/number/branded-types/positive-uint16.d.mts +4 -2
- package/dist/number/branded-types/positive-uint16.d.mts.map +1 -1
- package/dist/number/branded-types/positive-uint32.d.mts +4 -2
- package/dist/number/branded-types/positive-uint32.d.mts.map +1 -1
- package/dist/number/branded-types/safe-int.d.mts +1 -2
- package/dist/number/branded-types/safe-int.d.mts.map +1 -1
- package/dist/number/branded-types/safe-int.mjs +2 -2
- package/dist/number/branded-types/safe-uint.d.mts +1 -1
- package/dist/number/branded-types/safe-uint.mjs +1 -1
- package/dist/number/branded-types/uint.d.mts +1 -1
- package/dist/number/branded-types/uint16.d.mts +1 -1
- package/dist/number/branded-types/uint32.d.mts +1 -1
- package/dist/number/index.d.mts +1 -0
- package/dist/number/index.d.mts.map +1 -1
- package/dist/number/index.mjs +6 -0
- package/dist/number/index.mjs.map +1 -1
- package/dist/number/num-arithmetic-types.d.mts +74 -0
- package/dist/number/num-arithmetic-types.d.mts.map +1 -0
- package/dist/number/num-arithmetic-types.mjs +2 -0
- package/dist/number/num-arithmetic-types.mjs.map +1 -0
- package/dist/number/num.d.mts +53 -9
- package/dist/number/num.d.mts.map +1 -1
- package/dist/number/num.mjs +67 -15
- package/dist/number/num.mjs.map +1 -1
- package/dist/number/refined-number-utils.d.mts +12 -9
- package/dist/number/refined-number-utils.d.mts.map +1 -1
- package/dist/number/refined-number-utils.mjs.map +1 -1
- package/dist/object/object.mjs +7 -1
- package/dist/object/object.mjs.map +1 -1
- package/dist/others/fast-deep-equal.d.mts.map +1 -1
- package/dist/others/fast-deep-equal.mjs +0 -1
- package/dist/others/fast-deep-equal.mjs.map +1 -1
- package/dist/others/unknown-to-string.mjs +2 -2
- package/dist/others/unknown-to-string.mjs.map +1 -1
- package/package.json +6 -6
- package/src/array/impl/array-utils-creation.mts +1 -1
- package/src/array/impl/array-utils-iterators.mts +1 -1
- package/src/array/impl/array-utils-reducing-value.mts +1 -2
- package/src/array/impl/array-utils-search.mts +0 -2
- package/src/array/impl/array-utils-set-op.mts +0 -1
- package/src/array/impl/array-utils-transformation.mts +8 -6
- package/src/array/impl/array-utils-validation.mts +1 -5
- package/src/collections/imap-mapped.mts +696 -34
- package/src/collections/imap-mapped.test.mts +61 -13
- package/src/collections/imap.mts +20 -9
- package/src/collections/imap.test.mts +9 -8
- package/src/collections/iset-mapped.mts +18 -10
- package/src/collections/iset-mapped.test.mts +6 -1
- package/src/collections/iset.mts +11 -4
- package/src/collections/iset.test.mts +6 -8
- package/src/collections/queue.mts +32 -32
- package/src/collections/stack.mts +26 -26
- package/src/functional/optional/impl/optional-flat-map.mts +2 -2
- package/src/functional/optional.test.mts +6 -4
- package/src/functional/pipe.mts +0 -1
- package/src/functional/pipe.test.mts +2 -1
- package/src/functional/result/impl/result-flat-map.mts +2 -2
- package/src/functional/result.test.mts +3 -2
- package/src/functional/ternary-result/impl/ternary-result-flat-map.mts +5 -3
- package/src/functional/ternary-result.test.mts +8 -3
- package/src/guard/has-key.mts +1 -3
- package/src/guard/is-non-null-object.mts +0 -1
- package/src/guard/key-is-in.mts +1 -3
- package/src/json/json.mts +5 -8
- package/src/json/json.test.mts +2 -2
- package/src/number/branded-types/index.mts +6 -0
- package/src/number/branded-types/negative-finite-number.mts +210 -0
- package/src/number/branded-types/negative-finite-number.test.mts +172 -0
- package/src/number/branded-types/negative-int.mts +160 -0
- package/src/number/branded-types/negative-int.test.mts +146 -0
- package/src/number/branded-types/negative-safe-int.mts +173 -0
- package/src/number/branded-types/negative-safe-int.test.mts +120 -0
- package/src/number/branded-types/non-positive-finite-number.mts +191 -0
- package/src/number/branded-types/non-positive-finite-number.test.mts +286 -0
- package/src/number/branded-types/non-positive-int.mts +251 -0
- package/src/number/branded-types/non-positive-int.test.mts +197 -0
- package/src/number/branded-types/non-positive-safe-int.mts +147 -0
- package/src/number/branded-types/non-positive-safe-int.test.mts +223 -0
- package/src/number/branded-types/non-zero-finite-number.mts +14 -22
- package/src/number/branded-types/non-zero-finite-number.test.mts +0 -12
- package/src/number/branded-types/non-zero-int.mts +10 -32
- package/src/number/branded-types/non-zero-int.test.mts +0 -20
- package/src/number/branded-types/non-zero-int16.mts +8 -32
- package/src/number/branded-types/non-zero-int16.test.mts +0 -30
- package/src/number/branded-types/non-zero-int32.mts +8 -32
- package/src/number/branded-types/non-zero-int32.test.mts +0 -30
- package/src/number/branded-types/non-zero-safe-int.mts +10 -37
- package/src/number/branded-types/non-zero-safe-int.test.mts +0 -32
- package/src/number/branded-types/non-zero-uint16.mts +8 -22
- package/src/number/branded-types/non-zero-uint16.test.mts +0 -16
- package/src/number/branded-types/non-zero-uint32.mts +8 -22
- package/src/number/branded-types/non-zero-uint32.test.mts +0 -16
- package/src/number/branded-types/positive-safe-int.mts +1 -1
- package/src/number/branded-types/safe-int.mts +2 -2
- package/src/number/branded-types/safe-uint.mts +1 -1
- package/src/number/index.mts +1 -0
- package/src/number/num-arithmetic-types.mts +265 -0
- package/src/number/num-arithmetic-types.test.mts +130 -0
- package/src/number/num-arithmetic.test.mts +102 -0
- package/src/number/num.mts +99 -18
- package/src/number/num.test.mts +2 -2
- package/src/number/refined-number-utils.mts +19 -9
- package/src/object/object.mts +2 -2
- package/src/others/fast-deep-equal.mts +0 -2
- package/src/others/map-nullable.test.mts +4 -3
- package/src/others/unknown-to-string.mts +2 -2
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type FiniteNumber,
|
|
3
|
+
type Int,
|
|
4
|
+
type NegativeFiniteNumber,
|
|
5
|
+
type NegativeInt,
|
|
6
|
+
type NonNegativeFiniteNumber,
|
|
7
|
+
type NonPositiveInt,
|
|
8
|
+
type NonZeroInt,
|
|
9
|
+
type PositiveFiniteNumber,
|
|
10
|
+
type PositiveInt,
|
|
11
|
+
type PositiveSafeInt,
|
|
12
|
+
type SafeUint,
|
|
13
|
+
type Uint,
|
|
14
|
+
} from 'ts-type-forge';
|
|
15
|
+
import { expectType } from '../expect-type.mjs';
|
|
16
|
+
import {
|
|
17
|
+
type AddResult,
|
|
18
|
+
type DivIntResult,
|
|
19
|
+
type DivResult,
|
|
20
|
+
type LevelOf,
|
|
21
|
+
type MulResult,
|
|
22
|
+
type SignOf,
|
|
23
|
+
type SubResult,
|
|
24
|
+
} from './num-arithmetic-types.mjs';
|
|
25
|
+
|
|
26
|
+
describe('NumericArithmetic type-level', () => {
|
|
27
|
+
test('SignOf', () => {
|
|
28
|
+
expectType<SignOf<PositiveInt>, 'pos'>('=');
|
|
29
|
+
|
|
30
|
+
expectType<SignOf<NegativeInt>, 'neg'>('=');
|
|
31
|
+
|
|
32
|
+
expectType<SignOf<Uint>, 'nonneg'>('=');
|
|
33
|
+
|
|
34
|
+
expectType<SignOf<NonPositiveInt>, 'nonpos'>('=');
|
|
35
|
+
|
|
36
|
+
expectType<SignOf<NonZeroInt>, 'nonzero'>('=');
|
|
37
|
+
|
|
38
|
+
expectType<SignOf<Int>, 'any'>('=');
|
|
39
|
+
|
|
40
|
+
expectType<SignOf<number>, 'any'>('=');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('LevelOf', () => {
|
|
44
|
+
expectType<LevelOf<PositiveSafeInt>, 'safeint'>('=');
|
|
45
|
+
|
|
46
|
+
expectType<LevelOf<PositiveInt>, 'int'>('=');
|
|
47
|
+
|
|
48
|
+
expectType<LevelOf<PositiveFiniteNumber>, 'finite'>('=');
|
|
49
|
+
|
|
50
|
+
expectType<LevelOf<number>, 'number'>('=');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('mul: sign flips', () => {
|
|
54
|
+
// <=0 * <=0 = >=0
|
|
55
|
+
expectType<MulResult<NonPositiveInt, NonPositiveInt>, Uint>('=');
|
|
56
|
+
|
|
57
|
+
// <0 * <0 = >0
|
|
58
|
+
expectType<MulResult<NegativeInt, NegativeInt>, PositiveInt>('=');
|
|
59
|
+
|
|
60
|
+
// >0 * <0 = <0
|
|
61
|
+
expectType<MulResult<PositiveInt, NegativeInt>, NegativeInt>('=');
|
|
62
|
+
|
|
63
|
+
// finite negatives -> positive finite
|
|
64
|
+
expectType<
|
|
65
|
+
MulResult<NegativeFiniteNumber, NegativeFiniteNumber>,
|
|
66
|
+
PositiveFiniteNumber
|
|
67
|
+
>('=');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('mul: safeint widens to int', () => {
|
|
71
|
+
expectType<MulResult<PositiveSafeInt, PositiveSafeInt>, PositiveInt>('=');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('add', () => {
|
|
75
|
+
expectType<AddResult<PositiveInt, PositiveInt>, PositiveInt>('=');
|
|
76
|
+
|
|
77
|
+
expectType<AddResult<Uint, Uint>, Uint>('=');
|
|
78
|
+
|
|
79
|
+
// positive + negative -> unknown sign
|
|
80
|
+
expectType<AddResult<PositiveInt, NegativeInt>, Int>('=');
|
|
81
|
+
|
|
82
|
+
// nonzero + nonzero -> can be 0
|
|
83
|
+
expectType<AddResult<NonZeroInt, NonZeroInt>, Int>('=');
|
|
84
|
+
|
|
85
|
+
// safeint widens
|
|
86
|
+
expectType<AddResult<SafeUint, SafeUint>, Uint>('=');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('sub', () => {
|
|
90
|
+
// same-sign sub -> unknown sign (the saturating case lives in branded)
|
|
91
|
+
expectType<SubResult<Uint, Uint>, Int>('=');
|
|
92
|
+
|
|
93
|
+
expectType<SubResult<NonPositiveInt, NonPositiveInt>, Int>('=');
|
|
94
|
+
|
|
95
|
+
// >=0 - <=0 = >=0
|
|
96
|
+
expectType<SubResult<Uint, NonPositiveInt>, Uint>('=');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('div (exact) -> finite level', () => {
|
|
100
|
+
expectType<DivResult<PositiveInt, PositiveInt>, PositiveFiniteNumber>('=');
|
|
101
|
+
|
|
102
|
+
expectType<
|
|
103
|
+
DivResult<NegativeFiniteNumber, NegativeFiniteNumber>,
|
|
104
|
+
PositiveFiniteNumber
|
|
105
|
+
>('=');
|
|
106
|
+
|
|
107
|
+
expectType<DivResult<NonPositiveInt, NegativeInt>, NonNegativeFiniteNumber>(
|
|
108
|
+
'=',
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('divInt (floor)', () => {
|
|
113
|
+
// <0 / <0 = >0 but floor can be 0 -> Uint
|
|
114
|
+
expectType<DivIntResult<NegativeInt, NegativeInt>, Uint>('=');
|
|
115
|
+
|
|
116
|
+
// >0 / >0 floor can be 0 -> Uint
|
|
117
|
+
expectType<DivIntResult<PositiveInt, PositiveInt>, Uint>('=');
|
|
118
|
+
|
|
119
|
+
// safeint preserved under floor division
|
|
120
|
+
expectType<DivIntResult<PositiveSafeInt, PositiveSafeInt>, SafeUint>('=');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('plain number stays number', () => {
|
|
124
|
+
expectType<AddResult<number, number>, number>('=');
|
|
125
|
+
|
|
126
|
+
expectType<MulResult<number, PositiveInt>, number>('=');
|
|
127
|
+
|
|
128
|
+
expectType<AddResult<FiniteNumber, FiniteNumber>, FiniteNumber>('=');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Int,
|
|
3
|
+
type NegativeInt,
|
|
4
|
+
type PositiveFiniteNumber,
|
|
5
|
+
type PositiveInt,
|
|
6
|
+
type Uint,
|
|
7
|
+
} from 'ts-type-forge';
|
|
8
|
+
import { expectType } from '../expect-type.mjs';
|
|
9
|
+
import {
|
|
10
|
+
asNegativeInt,
|
|
11
|
+
asNonPositiveInt,
|
|
12
|
+
asPositiveInt,
|
|
13
|
+
asUint,
|
|
14
|
+
} from './branded-types/index.mjs';
|
|
15
|
+
import { Num } from './num.mjs';
|
|
16
|
+
|
|
17
|
+
describe('Num arithmetic (general / sign-aware)', () => {
|
|
18
|
+
describe('Num.add', () => {
|
|
19
|
+
test('adds and keeps the value (no saturation)', () => {
|
|
20
|
+
expect(Num.add(asPositiveInt(4), asPositiveInt(5))).toBe(9);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('positive + negative is typed Int', () => {
|
|
24
|
+
const r = Num.add(asPositiveInt(4), asNegativeInt(-9));
|
|
25
|
+
|
|
26
|
+
expect(r).toBe(-5);
|
|
27
|
+
|
|
28
|
+
expectType<typeof r, Int>('=');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('positive + positive stays PositiveInt', () => {
|
|
32
|
+
expectType<
|
|
33
|
+
ReturnType<typeof Num.add<PositiveInt, PositiveInt>>,
|
|
34
|
+
PositiveInt
|
|
35
|
+
>('=');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('Num.sub', () => {
|
|
40
|
+
test('does not saturate (unlike branded Uint.sub)', () => {
|
|
41
|
+
const r = Num.sub(asUint(3), asUint(5));
|
|
42
|
+
|
|
43
|
+
expect(r).toBe(-2);
|
|
44
|
+
|
|
45
|
+
expectType<typeof r, Int>('=');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('Num.mul', () => {
|
|
50
|
+
test('negative * negative is positive (PositiveInt)', () => {
|
|
51
|
+
const r = Num.mul(asNegativeInt(-5), asNegativeInt(-3));
|
|
52
|
+
|
|
53
|
+
expect(r).toBe(15);
|
|
54
|
+
|
|
55
|
+
expectType<typeof r, PositiveInt>('=');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('positive * negative is negative (NegativeInt)', () => {
|
|
59
|
+
const r = Num.mul(asPositiveInt(5), asNegativeInt(-3));
|
|
60
|
+
|
|
61
|
+
expect(r).toBe(-15);
|
|
62
|
+
|
|
63
|
+
expectType<typeof r, NegativeInt>('=');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('non-positive * non-positive is non-negative (Uint)', () => {
|
|
67
|
+
const r = Num.mul(asNonPositiveInt(-4), asNonPositiveInt(-2));
|
|
68
|
+
|
|
69
|
+
expect(r).toBe(8);
|
|
70
|
+
|
|
71
|
+
expectType<typeof r, Uint>('=');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('Num.div (exact)', () => {
|
|
76
|
+
test('exact division keeps the fractional value', () => {
|
|
77
|
+
const r = Num.div(asPositiveInt(9), asPositiveInt(2));
|
|
78
|
+
|
|
79
|
+
expect(r).toBe(4.5);
|
|
80
|
+
|
|
81
|
+
expectType<typeof r, PositiveFiniteNumber>('=');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('negative / negative is positive', () => {
|
|
85
|
+
expect(Num.div(asNegativeInt(-10), asNegativeInt(-2))).toBe(5);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('Num.divInt (floor)', () => {
|
|
90
|
+
test('floor division of negatives is non-negative (Uint)', () => {
|
|
91
|
+
const r = Num.divInt(asNegativeInt(-7), asNegativeInt(-2));
|
|
92
|
+
|
|
93
|
+
expect(r).toBe(3);
|
|
94
|
+
|
|
95
|
+
expectType<typeof r, Uint>('=');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('floor can reach 0', () => {
|
|
99
|
+
expect(Num.divInt(asNegativeInt(-2), asNegativeInt(-3))).toBe(0);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
package/src/number/num.mts
CHANGED
|
@@ -19,6 +19,13 @@ import {
|
|
|
19
19
|
import { expectType } from '../expect-type.mjs';
|
|
20
20
|
import { Result } from '../functional/index.mjs';
|
|
21
21
|
import { type SmallPositiveInt } from '../types.mjs';
|
|
22
|
+
import {
|
|
23
|
+
type AddResult,
|
|
24
|
+
type DivIntResult,
|
|
25
|
+
type DivResult,
|
|
26
|
+
type MulResult,
|
|
27
|
+
type SubResult,
|
|
28
|
+
} from './num-arithmetic-types.mjs';
|
|
22
29
|
|
|
23
30
|
/**
|
|
24
31
|
* Namespace providing utility functions for number manipulation and validation.
|
|
@@ -127,7 +134,7 @@ export namespace Num {
|
|
|
127
134
|
? Result.err(
|
|
128
135
|
new Error(`safeParseInt: "${s}" is not a valid base-10 integer`),
|
|
129
136
|
)
|
|
130
|
-
: // eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
137
|
+
: // eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
131
138
|
Result.ok(Math.trunc(viaNumber) as Int);
|
|
132
139
|
};
|
|
133
140
|
|
|
@@ -207,7 +214,7 @@ export namespace Num {
|
|
|
207
214
|
? Result.err(
|
|
208
215
|
new Error(`safeParseFloat: "${s}" is not a valid finite number`),
|
|
209
216
|
)
|
|
210
|
-
: // eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
217
|
+
: // eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
211
218
|
Result.ok(viaNumber as FiniteNumber);
|
|
212
219
|
};
|
|
213
220
|
|
|
@@ -504,19 +511,86 @@ export namespace Num {
|
|
|
504
511
|
}
|
|
505
512
|
|
|
506
513
|
/**
|
|
507
|
-
*
|
|
514
|
+
* Adds two numbers, returning the tightest sound branded result type.
|
|
515
|
+
*
|
|
516
|
+
* The sign of the result is computed exactly (e.g. a non-negative plus a
|
|
517
|
+
* non-negative is non-negative); the integer/finite level is preserved, with
|
|
518
|
+
* `SafeInt` operands widening to `Int` because the sum may exceed the safe
|
|
519
|
+
* range. No clamping is performed — for a result that stays inside a bounded
|
|
520
|
+
* type, use that type's branded `add` instead.
|
|
521
|
+
*
|
|
522
|
+
* @param a - The first addend.
|
|
523
|
+
* @param b - The second addend.
|
|
524
|
+
* @returns `a + b`, typed as the tightest branded type guaranteed to contain
|
|
525
|
+
* it.
|
|
526
|
+
*/
|
|
527
|
+
export const add = <A extends number, B extends number>(
|
|
528
|
+
a: A,
|
|
529
|
+
b: B,
|
|
530
|
+
): AddResult<A, B> =>
|
|
531
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
532
|
+
(a + b) as AddResult<A, B>;
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Subtracts `b` from `a`, returning the tightest sound branded result type.
|
|
508
536
|
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
*
|
|
537
|
+
* The sign is computed exactly; the level is preserved (with `SafeInt`
|
|
538
|
+
* widening to `Int`). No clamping — for saturating subtraction that stays in a
|
|
539
|
+
* bounded type (e.g. `Uint.sub` clamping at `0`), use that type's branded
|
|
540
|
+
* `sub`.
|
|
512
541
|
*
|
|
513
|
-
* @param a - The
|
|
514
|
-
* @param b - The
|
|
515
|
-
* @returns
|
|
542
|
+
* @param a - The minuend.
|
|
543
|
+
* @param b - The subtrahend.
|
|
544
|
+
* @returns `a - b`, typed as the tightest branded type guaranteed to contain
|
|
545
|
+
* it.
|
|
516
546
|
*/
|
|
517
|
-
export const
|
|
518
|
-
|
|
519
|
-
|
|
547
|
+
export const sub = <A extends number, B extends number>(
|
|
548
|
+
a: A,
|
|
549
|
+
b: B,
|
|
550
|
+
): SubResult<A, B> =>
|
|
551
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
552
|
+
(a - b) as SubResult<A, B>;
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Multiplies two numbers, returning the tightest sound branded result type.
|
|
556
|
+
*
|
|
557
|
+
* The sign follows the usual product rule (e.g. negative times negative is
|
|
558
|
+
* positive); the level is preserved (with `SafeInt` widening to `Int`). No
|
|
559
|
+
* clamping.
|
|
560
|
+
*
|
|
561
|
+
* @param a - The first factor.
|
|
562
|
+
* @param b - The second factor.
|
|
563
|
+
* @returns `a * b`, typed as the tightest branded type guaranteed to contain
|
|
564
|
+
* it.
|
|
565
|
+
*/
|
|
566
|
+
export const mul = <A extends number, B extends number>(
|
|
567
|
+
a: A,
|
|
568
|
+
b: B,
|
|
569
|
+
): MulResult<A, B> =>
|
|
570
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
571
|
+
(a * b) as MulResult<A, B>;
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Performs type-safe exact division with a compile-time zero-check.
|
|
575
|
+
*
|
|
576
|
+
* The divisor must be typed as `NonZeroNumber` or a non-zero numeric literal.
|
|
577
|
+
* The result sign follows the quotient rule; because exact division can be
|
|
578
|
+
* fractional, the level is `finite` (or `number`).
|
|
579
|
+
*
|
|
580
|
+
* @param a - The dividend.
|
|
581
|
+
* @param b - The divisor (must be non-zero, enforced by types).
|
|
582
|
+
* @returns `a / b`, typed as the tightest branded type guaranteed to contain
|
|
583
|
+
* it.
|
|
584
|
+
*/
|
|
585
|
+
export const div = <
|
|
586
|
+
A extends number,
|
|
587
|
+
B extends NonZeroNumber | SmallInt<'!=0'>,
|
|
588
|
+
>(
|
|
589
|
+
a: A,
|
|
590
|
+
b: B,
|
|
591
|
+
): DivResult<A, B> =>
|
|
592
|
+
// eslint-disable-next-line total-functions/no-partial-division, total-functions/no-unsafe-type-assertion
|
|
593
|
+
(a / b) as DivResult<A, B>;
|
|
520
594
|
|
|
521
595
|
/**
|
|
522
596
|
* Performs integer division using floor division.
|
|
@@ -531,12 +605,19 @@ export namespace Num {
|
|
|
531
605
|
* @param b - The divisor
|
|
532
606
|
* @returns The integer quotient, or `NaN` if b is zero
|
|
533
607
|
*/
|
|
534
|
-
export const divInt =
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
608
|
+
export const divInt = <
|
|
609
|
+
A extends number,
|
|
610
|
+
B extends NonZeroNumber | SmallInt<'!=0'>,
|
|
611
|
+
>(
|
|
612
|
+
a: A,
|
|
613
|
+
b: B,
|
|
614
|
+
): DivIntResult<A, B> => {
|
|
538
615
|
// eslint-disable-next-line total-functions/no-partial-division
|
|
539
|
-
Math.floor(Math.floor(a) / Math.floor(b));
|
|
616
|
+
const quotient = Math.floor(Math.floor(a) / Math.floor(b));
|
|
617
|
+
|
|
618
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
619
|
+
return quotient as DivIntResult<A, B>;
|
|
620
|
+
};
|
|
540
621
|
|
|
541
622
|
/**
|
|
542
623
|
* Rounds a number to a specified number of decimal places.
|
|
@@ -568,7 +649,7 @@ export namespace Num {
|
|
|
568
649
|
* @param num - The number to round
|
|
569
650
|
* @returns The rounded integer as an Int branded type
|
|
570
651
|
*/
|
|
571
|
-
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
652
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
572
653
|
export const roundToInt = (num: number): Int => Math.trunc(num + 0.5) as Int;
|
|
573
654
|
|
|
574
655
|
/**
|
package/src/number/num.test.mts
CHANGED
|
@@ -164,7 +164,7 @@ describe('Num test', () => {
|
|
|
164
164
|
test('rejects empty / whitespace-only input (unlike Number)', () => {
|
|
165
165
|
assert.isTrue(Result.isErr(Num.safeParseInt('')));
|
|
166
166
|
|
|
167
|
-
assert.isTrue(Result.isErr(Num.safeParseInt('
|
|
167
|
+
assert.isTrue(Result.isErr(Num.safeParseInt(' '.repeat(3))));
|
|
168
168
|
});
|
|
169
169
|
|
|
170
170
|
test('rejects non-finite words', () => {
|
|
@@ -222,7 +222,7 @@ describe('Num test', () => {
|
|
|
222
222
|
test('rejects empty / whitespace-only input (unlike Number)', () => {
|
|
223
223
|
assert.isTrue(Result.isErr(Num.safeParseFloat('')));
|
|
224
224
|
|
|
225
|
-
assert.isTrue(Result.isErr(Num.safeParseFloat('
|
|
225
|
+
assert.isTrue(Result.isErr(Num.safeParseFloat(' '.repeat(3))));
|
|
226
226
|
});
|
|
227
227
|
|
|
228
228
|
test('rejects non-finite values', () => {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type IntersectBrand,
|
|
7
7
|
type NonNegativeNumber,
|
|
8
8
|
type NonZeroNumber,
|
|
9
|
+
type PositiveNumber,
|
|
9
10
|
type RelaxedExclude,
|
|
10
11
|
type TypeEq,
|
|
11
12
|
type UnknownBrand,
|
|
@@ -84,10 +85,10 @@ export namespace TsDataForgeInternals {
|
|
|
84
85
|
* @template N - A branded number type
|
|
85
86
|
* @internal
|
|
86
87
|
*/
|
|
87
|
-
export type ToNonNegative<N extends UnknownNumberBrand> =
|
|
88
|
-
N
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
export type ToNonNegative<N extends UnknownNumberBrand> =
|
|
89
|
+
'!=0' extends UnwrapBrandTrueKeys<N>
|
|
90
|
+
? IntersectBrand<N, PositiveNumber>
|
|
91
|
+
: IntersectBrand<N, NonNegativeNumber>;
|
|
91
92
|
|
|
92
93
|
/**
|
|
93
94
|
* Removes the non-zero brand constraint from a branded number type. Used
|
|
@@ -99,7 +100,7 @@ export namespace TsDataForgeInternals {
|
|
|
99
100
|
export type RemoveNonZeroBrandKey<N extends UnknownNumberBrand> = Brand<
|
|
100
101
|
GetBrandValuePart<N>,
|
|
101
102
|
RelaxedExclude<UnwrapBrandTrueKeys<N>, '!=0'> & string,
|
|
102
|
-
UnwrapBrandFalseKeys<N> & string
|
|
103
|
+
RelaxedExclude<UnwrapBrandFalseKeys<N>, '<=0' | '>=0'> & string
|
|
103
104
|
>;
|
|
104
105
|
|
|
105
106
|
type CastToInt<N> = N extends Int ? N : never;
|
|
@@ -120,9 +121,13 @@ export namespace TsDataForgeInternals {
|
|
|
120
121
|
* 'positive' | 'range'
|
|
121
122
|
* @internal
|
|
122
123
|
*/
|
|
124
|
+
/** The closed binary arithmetic operations a branded namespace may expose. */
|
|
125
|
+
export type NumberClassBinOp = 'add' | 'div' | 'mul' | 'sub';
|
|
126
|
+
|
|
123
127
|
export type NumberClass<
|
|
124
128
|
N extends UnknownNumberBrand,
|
|
125
129
|
classes extends 'int' | 'non-negative' | 'positive' | 'range',
|
|
130
|
+
binOps extends NumberClassBinOp = NumberClassBinOp,
|
|
126
131
|
> = ('int' extends classes
|
|
127
132
|
? unknown
|
|
128
133
|
: 'positive' extends classes
|
|
@@ -162,16 +167,21 @@ export namespace TsDataForgeInternals {
|
|
|
162
167
|
clamp: (a: number) => N;
|
|
163
168
|
}>
|
|
164
169
|
: unknown) &
|
|
170
|
+
Pick<
|
|
171
|
+
Readonly<{
|
|
172
|
+
add: (x: N, y: N) => N;
|
|
173
|
+
sub: (x: N, y: N) => N;
|
|
174
|
+
mul: (x: N, y: N) => N;
|
|
175
|
+
div: (x: N, y: ToNonZero<N>) => N;
|
|
176
|
+
}>,
|
|
177
|
+
binOps
|
|
178
|
+
> &
|
|
165
179
|
Readonly<{
|
|
166
180
|
is: (a: number) => a is N;
|
|
167
181
|
min: (...values: readonly N[]) => N;
|
|
168
182
|
max: (...values: readonly N[]) => N;
|
|
169
183
|
random: (min: N, max: N) => N;
|
|
170
184
|
pow: (x: N, y: N) => N;
|
|
171
|
-
add: (x: N, y: N) => N;
|
|
172
|
-
sub: (x: N, y: N) => N;
|
|
173
|
-
mul: (x: N, y: N) => N;
|
|
174
|
-
div: (x: N, y: ToNonZero<N>) => N;
|
|
175
185
|
}>;
|
|
176
186
|
|
|
177
187
|
type BaseKeys =
|
package/src/object/object.mts
CHANGED
|
@@ -505,10 +505,10 @@ const deepOmitImpl = (
|
|
|
505
505
|
|
|
506
506
|
const head = path[0];
|
|
507
507
|
|
|
508
|
-
const tail = path.slice(1);
|
|
509
|
-
|
|
510
508
|
if (!hasKey(record, head)) return record;
|
|
511
509
|
|
|
510
|
+
const tail = path.slice(1);
|
|
511
|
+
|
|
512
512
|
if (!Arr.isNonEmpty(tail)) {
|
|
513
513
|
return Object.fromEntries(
|
|
514
514
|
Object.entries(record).filter(([k]) => k !== head),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type DeepReadonly } from 'ts-type-forge';
|
|
2
2
|
import { expectType } from '../expect-type.mjs';
|
|
3
|
-
import { pipe } from '../functional/index.mjs';
|
|
3
|
+
import { pipe, Result } from '../functional/index.mjs';
|
|
4
|
+
import { Num } from '../number/index.mjs';
|
|
4
5
|
import { mapNullable } from './map-nullable.mjs';
|
|
5
6
|
|
|
6
7
|
describe(mapNullable, () => {
|
|
@@ -306,7 +307,7 @@ describe(mapNullable, () => {
|
|
|
306
307
|
const extractAge = mapNullable((data: FormDataType) => data.age);
|
|
307
308
|
|
|
308
309
|
const parseAge = mapNullable((ageStr: string) =>
|
|
309
|
-
|
|
310
|
+
Result.unwrapOkOr(Num.safeParseInt(ageStr), Number.NaN),
|
|
310
311
|
);
|
|
311
312
|
|
|
312
313
|
const validateAge = mapNullable((age: number) =>
|
|
@@ -336,7 +337,7 @@ describe(mapNullable, () => {
|
|
|
336
337
|
const extractAge = mapNullable((data: FormDataType) => data.age);
|
|
337
338
|
|
|
338
339
|
const parseAge = mapNullable((ageStr: string) =>
|
|
339
|
-
|
|
340
|
+
Result.unwrapOkOr(Num.safeParseInt(ageStr), Number.NaN),
|
|
340
341
|
);
|
|
341
342
|
|
|
342
343
|
const extractedAge = extractAge(incompleteFormData);
|
|
@@ -78,7 +78,7 @@ export const unknownToString = (
|
|
|
78
78
|
|
|
79
79
|
// Special handling for Map
|
|
80
80
|
if (isMap(value)) {
|
|
81
|
-
const entries = Array.from(value
|
|
81
|
+
const entries = Array.from(value);
|
|
82
82
|
|
|
83
83
|
const entriesStr = prettyPrintObject
|
|
84
84
|
? JSON.stringify(entries, undefined, 2)
|
|
@@ -89,7 +89,7 @@ export const unknownToString = (
|
|
|
89
89
|
|
|
90
90
|
// Special handling for Set
|
|
91
91
|
if (isSet(value)) {
|
|
92
|
-
const values = Array.from(value
|
|
92
|
+
const values = Array.from(value);
|
|
93
93
|
|
|
94
94
|
const valuesStr = prettyPrintObject
|
|
95
95
|
? JSON.stringify(values, undefined, 2)
|