ts-data-forge 14.0.0 → 14.1.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/README.md +8 -7
- package/dist/array/impl/array-utils-modification.d.mts +18 -5
- package/dist/array/impl/array-utils-modification.d.mts.map +1 -1
- package/dist/array/impl/array-utils-modification.mjs.map +1 -1
- package/dist/array/impl/array-utils-slice-clamped.d.mts.map +1 -1
- package/dist/array/impl/array-utils-slice-clamped.mjs.map +1 -1
- package/dist/array/impl/array-utils-transformation.d.mts +12 -4
- package/dist/array/impl/array-utils-transformation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-transformation.mjs +4 -5
- package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
- package/dist/array/impl/array-utils-validation.d.mts +8 -3
- package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-validation.mjs.map +1 -1
- package/dist/collections/imap-mapped.mjs +1 -1
- package/dist/collections/imap-mapped.mjs.map +1 -1
- package/dist/collections/imap.d.mts +1 -1
- package/dist/collections/imap.mjs +1 -1
- package/dist/collections/imap.mjs.map +1 -1
- package/dist/collections/iset-mapped.d.mts +2 -2
- package/dist/collections/iset-mapped.d.mts.map +1 -1
- 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.map +1 -1
- package/dist/functional/match.d.mts.map +1 -1
- package/dist/functional/match.mjs.map +1 -1
- package/dist/number/branded-types/int.d.mts +1 -1
- package/dist/number/branded-types/int16.d.mts +1 -1
- package/dist/number/branded-types/int32.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.mjs +1 -1
- package/dist/number/branded-types/non-positive-finite-number.mjs.map +1 -1
- package/dist/number/branded-types/positive-int.d.mts +1 -1
- package/dist/number/branded-types/positive-int16.d.mts +1 -1
- package/dist/number/branded-types/positive-int32.d.mts +1 -1
- package/dist/number/branded-types/positive-safe-int.d.mts +1 -1
- package/dist/number/branded-types/positive-uint16.d.mts +1 -1
- package/dist/number/branded-types/positive-uint32.d.mts +1 -1
- package/dist/number/branded-types/safe-int.d.mts +1 -1
- package/dist/number/branded-types/safe-uint.d.mts +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/enum/int8.d.mts +2 -2
- package/dist/number/enum/int8.d.mts.map +1 -1
- package/dist/number/enum/int8.mjs.map +1 -1
- package/dist/number/enum/uint8.d.mts +2 -2
- package/dist/number/enum/uint8.d.mts.map +1 -1
- package/dist/number/enum/uint8.mjs.map +1 -1
- package/dist/number/num-arithmetic-types.d.mts +9 -10
- package/dist/number/num-arithmetic-types.d.mts.map +1 -1
- package/dist/number/refined-number-utils.d.mts +2 -2
- 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.d.mts +12 -12
- package/dist/object/object.d.mts.map +1 -1
- package/dist/object/object.mjs +5 -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 +2 -2
- package/dist/others/fast-deep-equal.mjs.map +1 -1
- package/package.json +4 -5
- package/src/array/impl/array-utils-creation.test.mts +10 -8
- package/src/array/impl/array-utils-length-constraint-preservation.test.mts +2 -2
- package/src/array/impl/array-utils-modification.mts +41 -33
- package/src/array/impl/array-utils-modification.test.mts +77 -4
- package/src/array/impl/array-utils-shape-invariants.test.mts +526 -0
- package/src/array/impl/array-utils-slice-clamped.mts +2 -1
- package/src/array/impl/array-utils-slicing.test.mts +11 -3
- package/src/array/impl/array-utils-transformation.mts +70 -20
- package/src/array/impl/array-utils-transformation.test.mts +44 -10
- package/src/array/impl/array-utils-validation.mts +54 -10
- package/src/array/impl/array-utils-validation.test.mts +153 -3
- package/src/collections/imap-mapped.mts +1 -1
- package/src/collections/imap-mapped.test.mts +2 -2
- package/src/collections/imap.mts +2 -2
- package/src/collections/iset-mapped.mts +7 -3
- package/src/collections/iset-mapped.test.mts +1 -1
- package/src/collections/iset.mts +7 -3
- package/src/collections/iset.test.mts +9 -6
- package/src/functional/match.mts +5 -4
- package/src/functional/result.test.mts +5 -2
- package/src/guard/is-primitive.test.mts +1 -1
- package/src/guard/is-record.test.mts +4 -4
- package/src/guard/is-type.test.mts +2 -2
- package/src/number/branded-types/finite-number.test.mts +2 -2
- package/src/number/branded-types/non-negative-finite-number.test.mts +2 -2
- package/src/number/branded-types/non-positive-finite-number.mts +1 -1
- package/src/number/branded-types/non-positive-finite-number.test.mts +5 -9
- package/src/number/branded-types/non-zero-finite-number.test.mts +2 -2
- package/src/number/branded-types/positive-finite-number.test.mts +2 -2
- package/src/number/enum/int8.mts +2 -1
- package/src/number/enum/uint8.mts +6 -2
- package/src/number/num-arithmetic-types.mts +129 -24
- package/src/number/num.test.mts +11 -11
- package/src/number/refined-number-utils.mts +2 -1
- package/src/object/object.mts +29 -17
- package/src/object/object.test.mts +2 -1
- package/src/others/fast-deep-equal.mts +6 -4
|
@@ -13,17 +13,13 @@ describe('NonPositiveFiniteNumber test', () => {
|
|
|
13
13
|
|
|
14
14
|
expect(() => asNonPositiveFiniteNumber(-1)).not.toThrow();
|
|
15
15
|
|
|
16
|
-
expect(() => asNonPositiveFiniteNumber(-
|
|
16
|
+
expect(() => asNonPositiveFiniteNumber(-1.23)).not.toThrow();
|
|
17
17
|
|
|
18
18
|
expect(() => asNonPositiveFiniteNumber(-0.5)).not.toThrow();
|
|
19
19
|
|
|
20
|
-
expect(() =>
|
|
21
|
-
asNonPositiveFiniteNumber(Number.MIN_VALUE * -1),
|
|
22
|
-
).not.toThrow();
|
|
20
|
+
expect(() => asNonPositiveFiniteNumber(-Number.MIN_VALUE)).not.toThrow();
|
|
23
21
|
|
|
24
|
-
expect(() =>
|
|
25
|
-
asNonPositiveFiniteNumber(Number.MAX_VALUE * -1),
|
|
26
|
-
).not.toThrow();
|
|
22
|
+
expect(() => asNonPositiveFiniteNumber(-Number.MAX_VALUE)).not.toThrow();
|
|
27
23
|
});
|
|
28
24
|
|
|
29
25
|
test('rejects positive numbers', () => {
|
|
@@ -63,11 +59,11 @@ describe('NonPositiveFiniteNumber test', () => {
|
|
|
63
59
|
|
|
64
60
|
assert.isTrue(isNonPositiveFiniteNumber(-1));
|
|
65
61
|
|
|
66
|
-
assert.isTrue(isNonPositiveFiniteNumber(-
|
|
62
|
+
assert.isTrue(isNonPositiveFiniteNumber(-1.23));
|
|
67
63
|
|
|
68
64
|
assert.isTrue(isNonPositiveFiniteNumber(-0.5));
|
|
69
65
|
|
|
70
|
-
assert.isTrue(isNonPositiveFiniteNumber(Number.MIN_VALUE
|
|
66
|
+
assert.isTrue(isNonPositiveFiniteNumber(-Number.MIN_VALUE));
|
|
71
67
|
});
|
|
72
68
|
|
|
73
69
|
test('correctly rejects positive numbers', () => {
|
|
@@ -13,7 +13,7 @@ describe('NonZeroFiniteNumber test', () => {
|
|
|
13
13
|
|
|
14
14
|
expect(() => asNonZeroFiniteNumber(-1)).not.toThrow();
|
|
15
15
|
|
|
16
|
-
expect(() => asNonZeroFiniteNumber(
|
|
16
|
+
expect(() => asNonZeroFiniteNumber(1.23)).not.toThrow();
|
|
17
17
|
|
|
18
18
|
expect(() => asNonZeroFiniteNumber(-2.5)).not.toThrow();
|
|
19
19
|
|
|
@@ -73,7 +73,7 @@ describe('NonZeroFiniteNumber test', () => {
|
|
|
73
73
|
|
|
74
74
|
assert.isTrue(isNonZeroFiniteNumber(-1));
|
|
75
75
|
|
|
76
|
-
assert.isTrue(isNonZeroFiniteNumber(
|
|
76
|
+
assert.isTrue(isNonZeroFiniteNumber(1.23));
|
|
77
77
|
|
|
78
78
|
assert.isTrue(isNonZeroFiniteNumber(-2.5));
|
|
79
79
|
|
|
@@ -11,7 +11,7 @@ describe('PositiveFiniteNumber test', () => {
|
|
|
11
11
|
test('accepts valid positive finite numbers', () => {
|
|
12
12
|
expect(() => asPositiveFiniteNumber(1)).not.toThrow();
|
|
13
13
|
|
|
14
|
-
expect(() => asPositiveFiniteNumber(
|
|
14
|
+
expect(() => asPositiveFiniteNumber(1.23)).not.toThrow();
|
|
15
15
|
|
|
16
16
|
expect(() => asPositiveFiniteNumber(0.5)).not.toThrow();
|
|
17
17
|
|
|
@@ -75,7 +75,7 @@ describe('PositiveFiniteNumber test', () => {
|
|
|
75
75
|
test('correctly identifies positive finite numbers', () => {
|
|
76
76
|
assert.isTrue(isPositiveFiniteNumber(1));
|
|
77
77
|
|
|
78
|
-
assert.isTrue(isPositiveFiniteNumber(
|
|
78
|
+
assert.isTrue(isPositiveFiniteNumber(1.23));
|
|
79
79
|
|
|
80
80
|
assert.isTrue(isPositiveFiniteNumber(0.5));
|
|
81
81
|
|
package/src/number/enum/int8.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AbsoluteValue,
|
|
3
3
|
type Int16,
|
|
4
|
+
type StrictExclude,
|
|
4
5
|
type Int8 as TtfImported_Int8,
|
|
5
6
|
} from 'ts-type-forge';
|
|
6
7
|
import { expectType } from '../../expect-type.mjs';
|
|
@@ -54,7 +55,7 @@ const sub = (x: Int8, y: Int8): Int8 => fromNumber(x - y);
|
|
|
54
55
|
|
|
55
56
|
const mul = (x: Int8, y: Int8): Int8 => fromNumber(x * y);
|
|
56
57
|
|
|
57
|
-
const div = (x: Int8, y:
|
|
58
|
+
const div = (x: Int8, y: StrictExclude<Int8, 0>): Int8 =>
|
|
58
59
|
fromNumber(Math.floor(x / y));
|
|
59
60
|
|
|
60
61
|
const random = (min: Int8, max: Int8): Int8 =>
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type StrictExclude,
|
|
3
|
+
type Uint8 as TtfImported_Uint8,
|
|
4
|
+
type Uint16,
|
|
5
|
+
} from 'ts-type-forge';
|
|
2
6
|
import { expectType } from '../../expect-type.mjs';
|
|
3
7
|
import { TsDataForgeInternals } from '../refined-number-utils.mjs';
|
|
4
8
|
|
|
@@ -44,7 +48,7 @@ const sub = (x: Uint8, y: Uint8): Uint8 => fromNumber(x - y);
|
|
|
44
48
|
|
|
45
49
|
const mul = (x: Uint8, y: Uint8): Uint8 => fromNumber(x * y);
|
|
46
50
|
|
|
47
|
-
const div = (x: Uint8, y:
|
|
51
|
+
const div = (x: Uint8, y: StrictExclude<Uint8, 0>): Uint8 =>
|
|
48
52
|
fromNumber(Math.floor(x / y));
|
|
49
53
|
|
|
50
54
|
const random = (min: Uint8, max: Uint8): Uint8 =>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type BoolAnd,
|
|
3
|
+
type BoolOr,
|
|
2
4
|
type FiniteNumber,
|
|
3
5
|
type Int,
|
|
4
6
|
type NegativeFiniteNumber,
|
|
@@ -6,6 +8,7 @@ import {
|
|
|
6
8
|
type NegativeNumber,
|
|
7
9
|
type NegativeSafeInt,
|
|
8
10
|
type NonNegativeFiniteNumber,
|
|
11
|
+
type NonNegativeInt,
|
|
9
12
|
type NonNegativeNumber,
|
|
10
13
|
type NonPositiveFiniteNumber,
|
|
11
14
|
type NonPositiveInt,
|
|
@@ -23,6 +26,7 @@ import {
|
|
|
23
26
|
type SafeUint,
|
|
24
27
|
type Uint,
|
|
25
28
|
} from 'ts-type-forge';
|
|
29
|
+
import { expectType } from '../expect-type.mjs';
|
|
26
30
|
|
|
27
31
|
/**
|
|
28
32
|
* Internal type-level arithmetic for {@link Num}'s `add`/`sub`/`mul`/`div`.
|
|
@@ -75,14 +79,6 @@ export type LevelOf<N> = [N] extends [SafeInt]
|
|
|
75
79
|
|
|
76
80
|
// --- boolean fact algebra over a sign ----------------------------------------
|
|
77
81
|
|
|
78
|
-
type And<A extends boolean, B extends boolean> = [A, B] extends [true, true]
|
|
79
|
-
? true
|
|
80
|
-
: false;
|
|
81
|
-
|
|
82
|
-
type Or<A extends boolean, B extends boolean> = true extends A | B
|
|
83
|
-
? true
|
|
84
|
-
: false;
|
|
85
|
-
|
|
86
82
|
/** Whether the value is guaranteed `>= 0`. */
|
|
87
83
|
type Ge0<S extends Sign> = S extends 'pos' | 'nonneg' ? true : false;
|
|
88
84
|
|
|
@@ -93,7 +89,7 @@ type Le0<S extends Sign> = S extends 'neg' | 'nonpos' ? true : false;
|
|
|
93
89
|
type Ne0<S extends Sign> = S extends 'pos' | 'neg' | 'nonzero' ? true : false;
|
|
94
90
|
|
|
95
91
|
/** Rebuilds a {@link Sign} from the three facts. */
|
|
96
|
-
type
|
|
92
|
+
type SignFromFacts<
|
|
97
93
|
ge0 extends boolean,
|
|
98
94
|
le0 extends boolean,
|
|
99
95
|
ne0 extends boolean,
|
|
@@ -109,16 +105,16 @@ type FromFacts<
|
|
|
109
105
|
? 'nonzero'
|
|
110
106
|
: 'any';
|
|
111
107
|
|
|
112
|
-
type NegateSign<S extends Sign> =
|
|
108
|
+
type NegateSign<S extends Sign> = SignFromFacts<Le0<S>, Ge0<S>, Ne0<S>>;
|
|
113
109
|
|
|
114
110
|
// --- sign combination per operation ------------------------------------------
|
|
115
111
|
|
|
116
|
-
type AddSign<A extends Sign, B extends Sign> =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
112
|
+
type AddSign<A extends Sign, B extends Sign> = SignFromFacts<
|
|
113
|
+
BoolAnd<Ge0<A>, Ge0<B>>,
|
|
114
|
+
BoolAnd<Le0<A>, Le0<B>>,
|
|
115
|
+
BoolOr<
|
|
116
|
+
BoolAnd<BoolAnd<Ge0<A>, Ge0<B>>, BoolOr<Ne0<A>, Ne0<B>>>,
|
|
117
|
+
BoolAnd<BoolAnd<Le0<A>, Le0<B>>, BoolOr<Ne0<A>, Ne0<B>>>
|
|
122
118
|
>
|
|
123
119
|
>;
|
|
124
120
|
|
|
@@ -129,16 +125,16 @@ type MulSignWithNe0<
|
|
|
129
125
|
A extends Sign,
|
|
130
126
|
B extends Sign,
|
|
131
127
|
ne0 extends boolean,
|
|
132
|
-
> =
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
> = SignFromFacts<
|
|
129
|
+
BoolOr<BoolAnd<Ge0<A>, Ge0<B>>, BoolAnd<Le0<A>, Le0<B>>>,
|
|
130
|
+
BoolOr<BoolAnd<Ge0<A>, Le0<B>>, BoolAnd<Le0<A>, Ge0<B>>>,
|
|
135
131
|
ne0
|
|
136
132
|
>;
|
|
137
133
|
|
|
138
134
|
type MulSign<A extends Sign, B extends Sign> = MulSignWithNe0<
|
|
139
135
|
A,
|
|
140
136
|
B,
|
|
141
|
-
|
|
137
|
+
BoolAnd<Ne0<A>, Ne0<B>>
|
|
142
138
|
>;
|
|
143
139
|
|
|
144
140
|
/** Exact division: nonzero dividend over nonzero divisor stays nonzero. */
|
|
@@ -158,9 +154,12 @@ type LooserLevel<LA extends Level, LB extends Level> = 'number' extends LA | LB
|
|
|
158
154
|
? 'int'
|
|
159
155
|
: 'safeint';
|
|
160
156
|
|
|
157
|
+
type WidenSafeIntToInt<T> = T extends 'safeint' ? 'int' : T;
|
|
158
|
+
|
|
161
159
|
/** `+`/`-`/`*`: looser level, widening `safeint -> int` (overflow may occur). */
|
|
162
|
-
type AddMulLevel<LA extends Level, LB extends Level> =
|
|
163
|
-
LooserLevel<LA, LB>
|
|
160
|
+
type AddMulLevel<LA extends Level, LB extends Level> = WidenSafeIntToInt<
|
|
161
|
+
LooserLevel<LA, LB>
|
|
162
|
+
>;
|
|
164
163
|
|
|
165
164
|
/** Exact division yields a (possibly fractional) finite number. */
|
|
166
165
|
type DivLevel<LA extends Level, LB extends Level> = 'number' extends LA | LB
|
|
@@ -170,8 +169,10 @@ type DivLevel<LA extends Level, LB extends Level> = 'number' extends LA | LB
|
|
|
170
169
|
/** Floor division yields an integer; the safe range is preserved. */
|
|
171
170
|
type DivIntLevel<LA extends Level, LB extends Level> = 'number' extends LA | LB
|
|
172
171
|
? 'number'
|
|
173
|
-
: [LA
|
|
174
|
-
? 'safeint'
|
|
172
|
+
: [LA] extends ['safeint']
|
|
173
|
+
? [LB] extends ['safeint']
|
|
174
|
+
? 'safeint'
|
|
175
|
+
: 'int'
|
|
175
176
|
: 'int';
|
|
176
177
|
|
|
177
178
|
// --- reconstruction (sign, level) -> canonical brand -------------------------
|
|
@@ -263,3 +264,107 @@ export type DivIntResult<A, B> = Reconstruct<
|
|
|
263
264
|
DivIntSign<SignOf<A>, SignOf<B>>,
|
|
264
265
|
DivIntLevel<LevelOf<A>, LevelOf<B>>
|
|
265
266
|
>;
|
|
267
|
+
|
|
268
|
+
// #region type tests
|
|
269
|
+
{
|
|
270
|
+
type A = NonPositiveInt;
|
|
271
|
+
|
|
272
|
+
type B = NonPositiveInt;
|
|
273
|
+
|
|
274
|
+
expectType<MulResult<A, B>, Uint>('=');
|
|
275
|
+
|
|
276
|
+
type SignA = SignOf<A>;
|
|
277
|
+
|
|
278
|
+
type SignB = SignOf<B>;
|
|
279
|
+
|
|
280
|
+
expectType<SignA, 'nonpos'>('=');
|
|
281
|
+
|
|
282
|
+
expectType<MulSign<SignA, SignB>, 'nonneg'>('=');
|
|
283
|
+
|
|
284
|
+
expectType<BoolAnd<Ne0<SignA>, Ne0<SignB>>, false>('=');
|
|
285
|
+
|
|
286
|
+
expectType<MulSignWithNe0<SignA, SignB, false>, 'nonneg'>('=');
|
|
287
|
+
|
|
288
|
+
expectType<
|
|
289
|
+
BoolOr<BoolAnd<Ge0<SignA>, Ge0<SignB>>, BoolAnd<Le0<SignA>, Le0<SignB>>>,
|
|
290
|
+
true
|
|
291
|
+
>('=');
|
|
292
|
+
|
|
293
|
+
expectType<Ge0<SignA>, false>('=');
|
|
294
|
+
|
|
295
|
+
expectType<Ge0<SignB>, false>('=');
|
|
296
|
+
|
|
297
|
+
expectType<Le0<SignA>, true>('=');
|
|
298
|
+
|
|
299
|
+
expectType<Le0<SignB>, true>('=');
|
|
300
|
+
|
|
301
|
+
expectType<BoolAnd<Ge0<SignA>, Ge0<SignB>>, false>('=');
|
|
302
|
+
|
|
303
|
+
expectType<BoolAnd<true, true>, true>('=');
|
|
304
|
+
|
|
305
|
+
expectType<
|
|
306
|
+
BoolOr<BoolAnd<Ge0<SignA>, Le0<SignB>>, BoolAnd<Le0<SignA>, Ge0<SignB>>>,
|
|
307
|
+
false
|
|
308
|
+
>('=');
|
|
309
|
+
|
|
310
|
+
expectType<SignFromFacts<true, false, false>, 'nonneg'>('=');
|
|
311
|
+
|
|
312
|
+
expectType<AddMulLevel<LevelOf<A>, LevelOf<B>>, 'int'>('=');
|
|
313
|
+
|
|
314
|
+
type LA = LevelOf<A>;
|
|
315
|
+
|
|
316
|
+
type LB = LevelOf<B>;
|
|
317
|
+
|
|
318
|
+
expectType<LA, 'int'>('=');
|
|
319
|
+
|
|
320
|
+
expectType<LooserLevel<LA, LB>, 'int'>('=');
|
|
321
|
+
|
|
322
|
+
{
|
|
323
|
+
type MulResult2<X, Y> = Reconstruct<
|
|
324
|
+
MulSign<SignOf<X>, SignOf<Y>>,
|
|
325
|
+
AddMulLevel<LevelOf<X>, LevelOf<Y>>
|
|
326
|
+
>;
|
|
327
|
+
|
|
328
|
+
expectType<MulResult<A, B>, MulResult2<A, B>>('=');
|
|
329
|
+
|
|
330
|
+
expectType<
|
|
331
|
+
MulResult<A, B>,
|
|
332
|
+
Reconstruct<
|
|
333
|
+
MulSign<SignOf<A>, SignOf<B>>,
|
|
334
|
+
AddMulLevel<LevelOf<A>, LevelOf<B>>
|
|
335
|
+
>
|
|
336
|
+
>('=');
|
|
337
|
+
|
|
338
|
+
expectType<
|
|
339
|
+
Reconstruct<
|
|
340
|
+
MulSign<SignOf<A>, SignOf<B>>,
|
|
341
|
+
AddMulLevel<LevelOf<A>, LevelOf<B>>
|
|
342
|
+
>,
|
|
343
|
+
NonNegativeInt
|
|
344
|
+
>('=');
|
|
345
|
+
|
|
346
|
+
expectType<
|
|
347
|
+
Reconstruct<MulSign<SignA, SignB>, AddMulLevel<LA, LB>>,
|
|
348
|
+
NonNegativeInt
|
|
349
|
+
>('=');
|
|
350
|
+
|
|
351
|
+
expectType<Reconstruct<'nonneg', 'int'>, NonNegativeInt>('=');
|
|
352
|
+
|
|
353
|
+
expectType<MulResult<A, B>, NonNegativeInt>('=');
|
|
354
|
+
|
|
355
|
+
// type Reconstruct<S extends Sign, L extends Level> = L extends 'safeint'
|
|
356
|
+
// ? ReconstructSafeInt<S>
|
|
357
|
+
// : L extends 'int'
|
|
358
|
+
// ? ReconstructInt<S>
|
|
359
|
+
// : L extends 'finite'
|
|
360
|
+
// ? ReconstructFinite<S>
|
|
361
|
+
// : ReconstructNumber<S>;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// FromFacts<
|
|
365
|
+
// BoolOr<BoolAnd<Ge0<A>, Ge0<B>>, BoolAnd<Le0<A>, Le0<B>>>,
|
|
366
|
+
// BoolOr<BoolAnd<Ge0<A>, Le0<B>>, BoolAnd<Le0<A>, Ge0<B>>>,
|
|
367
|
+
// ne0
|
|
368
|
+
// >;
|
|
369
|
+
}
|
|
370
|
+
// #endregion
|
package/src/number/num.test.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type NonZeroNumber } from 'ts-type-forge';
|
|
1
|
+
import { type FixedLengthTuple, type NonZeroNumber } from 'ts-type-forge';
|
|
2
2
|
import { expectType } from '../expect-type.mjs';
|
|
3
3
|
import { pipe, Result } from '../functional/index.mjs';
|
|
4
4
|
import { asNonZeroFiniteNumber } from './branded-types/index.mjs';
|
|
5
5
|
import { Num } from './num.mjs';
|
|
6
6
|
|
|
7
7
|
const testClamp = (
|
|
8
|
-
[a, b]:
|
|
8
|
+
[a, b]: FixedLengthTuple<2, number>,
|
|
9
9
|
target: number,
|
|
10
10
|
expected: number,
|
|
11
11
|
): void => {
|
|
@@ -244,8 +244,8 @@ describe('Num test', () => {
|
|
|
244
244
|
});
|
|
245
245
|
|
|
246
246
|
test('composes with Result.unwrapOk + nullish fallback', () => {
|
|
247
|
-
expect(Result.unwrapOk(Num.safeParseFloat('
|
|
248
|
-
|
|
247
|
+
expect(Result.unwrapOk(Num.safeParseFloat('1.23')) ?? Number.NaN).toBe(
|
|
248
|
+
1.23,
|
|
249
249
|
);
|
|
250
250
|
|
|
251
251
|
expect(Result.unwrapOk(Num.safeParseFloat('')) ?? Number.NaN).toBeNaN();
|
|
@@ -360,9 +360,9 @@ describe('Num test', () => {
|
|
|
360
360
|
|
|
361
361
|
describe('roundAt', () => {
|
|
362
362
|
test('rounds to specified decimal places', () => {
|
|
363
|
-
expect(Num.roundAt(
|
|
363
|
+
expect(Num.roundAt(1.234_56, 2)).toBe(1.23);
|
|
364
364
|
|
|
365
|
-
expect(Num.roundAt(
|
|
365
|
+
expect(Num.roundAt(1.234_56, 3)).toBe(1.235);
|
|
366
366
|
|
|
367
367
|
expect(Num.roundAt(2.555, 2)).toBe(2.56);
|
|
368
368
|
|
|
@@ -388,21 +388,21 @@ describe('Num test', () => {
|
|
|
388
388
|
test('creates rounding function with specified precision', () => {
|
|
389
389
|
const round2 = Num.round(2);
|
|
390
390
|
|
|
391
|
-
expect(round2(
|
|
391
|
+
expect(round2(1.234_56)).toBe(1.23);
|
|
392
392
|
|
|
393
393
|
expect(round2(2.556)).toBe(2.56);
|
|
394
394
|
|
|
395
395
|
const round1 = Num.round(1);
|
|
396
396
|
|
|
397
|
-
expect(round1(
|
|
397
|
+
expect(round1(1.234_56)).toBe(1.2);
|
|
398
398
|
|
|
399
399
|
expect(round1(2.56)).toBe(2.6);
|
|
400
400
|
|
|
401
401
|
const round3 = Num.round(3);
|
|
402
402
|
|
|
403
|
-
expect(round3(
|
|
403
|
+
expect(round3(1.2346)).toBe(1.235);
|
|
404
404
|
|
|
405
|
-
expect(round3(
|
|
405
|
+
expect(round3(4.5678)).toBe(4.568);
|
|
406
406
|
});
|
|
407
407
|
});
|
|
408
408
|
|
|
@@ -412,7 +412,7 @@ describe('Num test', () => {
|
|
|
412
412
|
|
|
413
413
|
expect(Num.mapNaN2Undefined(0)).toBe(0);
|
|
414
414
|
|
|
415
|
-
expect(Num.mapNaN2Undefined(-
|
|
415
|
+
expect(Num.mapNaN2Undefined(-1.23)).toBe(-1.23);
|
|
416
416
|
|
|
417
417
|
expect(Num.mapNaN2Undefined(Number.NaN)).toBeUndefined();
|
|
418
418
|
});
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type NonZeroNumber,
|
|
9
9
|
type PositiveNumber,
|
|
10
10
|
type RelaxedExclude,
|
|
11
|
+
type StrictPick,
|
|
11
12
|
type TypeEq,
|
|
12
13
|
type UnknownBrand,
|
|
13
14
|
type UnwrapBrandFalseKeys,
|
|
@@ -167,7 +168,7 @@ export namespace TsDataForgeInternals {
|
|
|
167
168
|
fromNumber: (a: number) => N;
|
|
168
169
|
}>
|
|
169
170
|
: unknown) &
|
|
170
|
-
|
|
171
|
+
StrictPick<
|
|
171
172
|
Readonly<{
|
|
172
173
|
add: (x: N, y: N) => N;
|
|
173
174
|
sub: (x: N, y: N) => N;
|
package/src/object/object.mts
CHANGED
|
@@ -7,7 +7,11 @@ import {
|
|
|
7
7
|
type IsUnion,
|
|
8
8
|
type List,
|
|
9
9
|
type ReadonlyRecord,
|
|
10
|
+
type RelaxedExclude,
|
|
11
|
+
type RelaxedOmit,
|
|
10
12
|
type RelaxedPick,
|
|
13
|
+
type StrictOmit,
|
|
14
|
+
type StrictPick,
|
|
11
15
|
type TypeEq,
|
|
12
16
|
type UnknownRecord,
|
|
13
17
|
} from 'ts-type-forge';
|
|
@@ -125,7 +129,7 @@ export namespace Obj {
|
|
|
125
129
|
export function pick<
|
|
126
130
|
const R extends UnknownRecord,
|
|
127
131
|
const Keys extends readonly (keyof R)[],
|
|
128
|
-
>(record: R, keys: Keys):
|
|
132
|
+
>(record: R, keys: Keys): StrictPick<R, ArrayElement<Keys>>;
|
|
129
133
|
|
|
130
134
|
// Curried version
|
|
131
135
|
export function pick<const Keys extends readonly PropertyKey[]>(
|
|
@@ -140,7 +144,7 @@ export namespace Obj {
|
|
|
140
144
|
>(
|
|
141
145
|
...args: readonly [record: R, keys: Keys] | readonly [keys: Keys]
|
|
142
146
|
):
|
|
143
|
-
|
|
|
147
|
+
| StrictPick<R, ArrayElement<Keys>>
|
|
144
148
|
| ((record: R) => RelaxedPick<R, ArrayElement<Keys>>) {
|
|
145
149
|
switch (args.length) {
|
|
146
150
|
case 2: {
|
|
@@ -212,12 +216,14 @@ export namespace Obj {
|
|
|
212
216
|
export function omit<
|
|
213
217
|
const R extends UnknownRecord,
|
|
214
218
|
const Keys extends readonly (keyof R)[],
|
|
215
|
-
>(record: R, keys: Keys):
|
|
219
|
+
>(record: R, keys: Keys): StrictOmit<R, ArrayElement<Keys>>;
|
|
216
220
|
|
|
217
221
|
// Curried version
|
|
218
222
|
export function omit<const Keys extends readonly PropertyKey[]>(
|
|
219
223
|
keys: Keys,
|
|
220
|
-
): <const R extends UnknownRecord>(
|
|
224
|
+
): <const R extends UnknownRecord>(
|
|
225
|
+
record: R,
|
|
226
|
+
) => RelaxedOmit<R, ArrayElement<Keys>>;
|
|
221
227
|
|
|
222
228
|
export function omit<
|
|
223
229
|
const R extends UnknownRecord,
|
|
@@ -226,7 +232,8 @@ export namespace Obj {
|
|
|
226
232
|
...args:
|
|
227
233
|
readonly [record: R, keys: Keys] | readonly [keys: readonly PropertyKey[]]
|
|
228
234
|
):
|
|
229
|
-
|
|
235
|
+
| StrictOmit<R, ArrayElement<Keys>>
|
|
236
|
+
| ((record: R) => StrictOmit<R, ArrayElement<Keys>>) {
|
|
230
237
|
switch (args.length) {
|
|
231
238
|
case 2: {
|
|
232
239
|
const [record, keys] = args;
|
|
@@ -246,10 +253,11 @@ export namespace Obj {
|
|
|
246
253
|
|
|
247
254
|
return <R2 extends UnknownRecord>(record: R2) => {
|
|
248
255
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
249
|
-
const result = omit(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
256
|
+
const result = omit(
|
|
257
|
+
record,
|
|
258
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
259
|
+
keys as readonly (keyof R2)[],
|
|
260
|
+
) as RelaxedOmit<R2, ArrayElement<Keys>>;
|
|
253
261
|
|
|
254
262
|
return result;
|
|
255
263
|
};
|
|
@@ -544,7 +552,7 @@ declare namespace TsDataForgeInternals {
|
|
|
544
552
|
TypeEq<Entries['length'], 0> extends true
|
|
545
553
|
? R
|
|
546
554
|
: EntriesToObjectImpl<
|
|
547
|
-
R &
|
|
555
|
+
R & ReadonlyRecord<Entries[0][0], Entries[0][1]>,
|
|
548
556
|
List.Tail<Entries>
|
|
549
557
|
>;
|
|
550
558
|
|
|
@@ -639,12 +647,12 @@ declare namespace TsDataForgeInternals {
|
|
|
639
647
|
type MergeTwo<A extends UnknownRecord, B extends UnknownRecord> = {
|
|
640
648
|
// 1. Required properties from B (override A completely)
|
|
641
649
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
642
|
-
readonly [K in keyof B as {} extends
|
|
650
|
+
readonly [K in keyof B as {} extends StrictPick<B, K> ? never : K]: B[K];
|
|
643
651
|
} & {
|
|
644
652
|
// 2. Optional properties from B that are NOT in A
|
|
645
653
|
readonly [
|
|
646
654
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
647
|
-
K in keyof B as {} extends
|
|
655
|
+
K in keyof B as {} extends StrictPick<B, K>
|
|
648
656
|
? K extends keyof A
|
|
649
657
|
? never
|
|
650
658
|
: K
|
|
@@ -654,7 +662,7 @@ declare namespace TsDataForgeInternals {
|
|
|
654
662
|
// 3. Optional properties from B that ARE in A (create union)
|
|
655
663
|
readonly [
|
|
656
664
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
657
|
-
K in keyof B as {} extends
|
|
665
|
+
K in keyof B as {} extends StrictPick<B, K>
|
|
658
666
|
? K extends keyof A
|
|
659
667
|
? K
|
|
660
668
|
: never
|
|
@@ -664,22 +672,26 @@ declare namespace TsDataForgeInternals {
|
|
|
664
672
|
// 4. Required properties only in A (not overridden by B)
|
|
665
673
|
readonly [
|
|
666
674
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
667
|
-
K in
|
|
675
|
+
K in RelaxedExclude<keyof A, keyof B> as {} extends StrictPick<A, K>
|
|
676
|
+
? never
|
|
677
|
+
: K
|
|
668
678
|
]: A[K];
|
|
669
679
|
} & {
|
|
670
680
|
// 5. Optional properties only in A (not overridden by B)
|
|
671
681
|
readonly [
|
|
672
682
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
673
|
-
K in
|
|
683
|
+
K in RelaxedExclude<keyof A, keyof B> as {} extends StrictPick<A, K>
|
|
684
|
+
? K
|
|
685
|
+
: never
|
|
674
686
|
]?: A[K];
|
|
675
687
|
} extends infer O
|
|
676
688
|
? Readonly<
|
|
677
689
|
{
|
|
678
690
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
679
|
-
[K in keyof O as {} extends
|
|
691
|
+
[K in keyof O as {} extends StrictPick<O, K> ? never : K]: O[K];
|
|
680
692
|
} & {
|
|
681
693
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
682
|
-
[K in keyof O as {} extends
|
|
694
|
+
[K in keyof O as {} extends StrictPick<O, K> ? K : never]?: O[K];
|
|
683
695
|
}
|
|
684
696
|
>
|
|
685
697
|
: never;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type DeepReadonly,
|
|
3
3
|
type FixedLengthTuple,
|
|
4
|
+
type ReadonlyRecord,
|
|
4
5
|
type UnknownRecord,
|
|
5
6
|
} from 'ts-type-forge';
|
|
6
7
|
import { expectType } from '../expect-type.mjs';
|
|
@@ -180,7 +181,7 @@ describe('fromEntries', () => {
|
|
|
180
181
|
] as const;
|
|
181
182
|
|
|
182
183
|
const result = Obj.fromEntries(dynamicEntries) satisfies Partial<
|
|
183
|
-
|
|
184
|
+
ReadonlyRecord<'name' | 'email', string>
|
|
184
185
|
>;
|
|
185
186
|
|
|
186
187
|
assert.deepStrictEqual(result, { name: 'Alice' });
|
|
@@ -39,9 +39,11 @@ export const fastDeepEqual = <T,>(a: T, b: T): boolean => {
|
|
|
39
39
|
return (
|
|
40
40
|
isMap(b) &&
|
|
41
41
|
a.size === b.size &&
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
a
|
|
43
|
+
.entries()
|
|
44
|
+
.every(
|
|
45
|
+
([key, value]) => b.has(key) && fastDeepEqual(value, b.get(key)),
|
|
46
|
+
)
|
|
45
47
|
);
|
|
46
48
|
}
|
|
47
49
|
|
|
@@ -49,7 +51,7 @@ export const fastDeepEqual = <T,>(a: T, b: T): boolean => {
|
|
|
49
51
|
return (
|
|
50
52
|
isSet(b) &&
|
|
51
53
|
a.size === b.size &&
|
|
52
|
-
|
|
54
|
+
a.entries().every(([value]) => b.has(value))
|
|
53
55
|
);
|
|
54
56
|
}
|
|
55
57
|
|