ts-fortress 7.2.0 → 7.3.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.
@@ -1,4 +1,14 @@
1
- import { Arr, isNumber } from 'ts-data-forge';
1
+ import { Arr, expectType, Int, isNumber, Result } from 'ts-data-forge';
2
+ import {
3
+ type Brand,
4
+ type FiniteNumber,
5
+ type IsNever,
6
+ type NegativeInt,
7
+ type NonZeroFiniteNumber,
8
+ type NonZeroInt,
9
+ type PositiveInt,
10
+ type Uint,
11
+ } from 'ts-type-forge';
2
12
  import { refine } from '../other-types/index.mjs';
3
13
  import { type Type } from '../type.mjs';
4
14
  import { createPrimitiveType } from '../utils/index.mjs';
@@ -8,7 +18,7 @@ export function number(defaultValue?: number): Type<number>;
8
18
  export function number<N extends number, const C extends Constraints>(
9
19
  defaultValue: N & DefaultValueType<N, C>,
10
20
  constraints: C,
11
- ): Type<number>;
21
+ ): Type<ConstraintsToResultType<C>>;
12
22
 
13
23
  export function number<C extends Constraints>(
14
24
  defaultValue: number = 0,
@@ -27,87 +37,18 @@ export function number<C extends Constraints>(
27
37
  return baseType;
28
38
  }
29
39
 
30
- if (constraints.gt !== undefined && !(defaultValue > constraints.gt)) {
31
- throw new Error(
32
- `defaultValue ${defaultValue} for number does not satisfy the constraint gt = ${constraints.gt}`,
33
- );
34
- }
35
-
36
- if (constraints.gte !== undefined && !(defaultValue >= constraints.gte)) {
37
- throw new Error(
38
- `defaultValue ${defaultValue} for number does not satisfy the constraint gte = ${constraints.gte}`,
39
- );
40
- }
41
-
42
- if (constraints.min !== undefined && !(defaultValue >= constraints.min)) {
43
- throw new Error(
44
- `defaultValue ${defaultValue} for number does not satisfy the constraint min = ${constraints.min}`,
45
- );
46
- }
47
-
48
- if (constraints.lt !== undefined && !(defaultValue < constraints.lt)) {
49
- throw new Error(
50
- `defaultValue ${defaultValue} for number does not satisfy the constraint lt = ${constraints.lt}`,
51
- );
52
- }
53
-
54
- if (constraints.lte !== undefined && !(defaultValue <= constraints.lte)) {
55
- throw new Error(
56
- `defaultValue ${defaultValue} for number does not satisfy the constraint lte = ${constraints.lte}`,
57
- );
58
- }
59
-
60
- if (constraints.max !== undefined && !(defaultValue <= constraints.max)) {
61
- throw new Error(
62
- `defaultValue ${defaultValue} for number does not satisfy the constraint max = ${constraints.max}`,
63
- );
64
- }
65
-
66
- if (constraints.positive === true && !(defaultValue > 0)) {
67
- throw new Error(
68
- `defaultValue ${defaultValue} for number does not satisfy the constraint positive = true`,
69
- );
70
- }
71
-
72
- if (constraints.nonNegative === true && !(defaultValue >= 0)) {
73
- throw new Error(
74
- `defaultValue ${defaultValue} for number does not satisfy the constraint nonNegative = true`,
75
- );
76
- }
40
+ const constraintsPredicate = createConstraintsPredicate(constraints);
77
41
 
78
- if (constraints.negative === true && !(defaultValue < 0)) {
79
- throw new Error(
80
- `defaultValue ${defaultValue} for number does not satisfy the constraint negative = true`,
81
- );
82
- }
83
-
84
- if (constraints.nonPositive === true && !(defaultValue <= 0)) {
85
- throw new Error(
86
- `defaultValue ${defaultValue} for number does not satisfy the constraint nonPositive = true`,
87
- );
88
- }
42
+ const defaultValueConstraintsCheck = constraintsPredicate(defaultValue);
89
43
 
90
- if (
91
- constraints.multipleOf !== undefined &&
92
- defaultValue % constraints.multipleOf !== 0
93
- ) {
94
- throw new Error(
95
- `defaultValue ${defaultValue} for number does not satisfy the constraint multipleOf = ${constraints.multipleOf}`,
96
- );
44
+ if (Result.isErr(defaultValueConstraintsCheck)) {
45
+ throw new Error(defaultValueConstraintsCheck.value);
97
46
  }
98
47
 
99
- if (constraints.step !== undefined && defaultValue % constraints.step !== 0) {
100
- throw new Error(
101
- `defaultValue ${defaultValue} for number does not satisfy the constraint step = ${constraints.step}`,
102
- );
103
- }
104
-
105
- const satisfiesConstraints = createConstraintsPredicate(constraints);
106
-
107
48
  return refine({
108
49
  baseType,
109
50
  defaultValue,
110
- is: (value): value is number => satisfiesConstraints(value),
51
+ is: (value): value is number => Result.isOk(constraintsPredicate(value)),
111
52
  typeName: 'number',
112
53
  });
113
54
  }
@@ -120,34 +61,152 @@ type Constraints = Partial<
120
61
  lt: number;
121
62
  lte: number;
122
63
  max: number;
123
- negative: boolean;
124
- nonNegative: boolean;
125
- positive: boolean;
126
- nonPositive: boolean;
127
64
  multipleOf: number;
128
65
  step: number;
66
+
67
+ finite: true;
68
+ int: true;
69
+ nonzero: true;
70
+ negative: true;
71
+ nonNegative: true;
72
+ positive: true;
73
+ nonPositive: true;
129
74
  }>
130
75
  >;
131
76
 
132
77
  type DefaultValueType<
133
78
  N extends number,
134
- R extends Constraints,
135
- > = DefaultValueWhenNegativeIsOn<N, R> &
136
- DefaultValueWhenNonNegativeIsOn<N, R> &
137
- DefaultValueWhenPositiveIsOn<N, R> &
138
- DefaultValueWhenNonPositiveIsOn<N, R>;
139
-
140
- type DefaultValueWhenNegativeIsOn<N extends number, R extends Constraints> =
141
- R extends Readonly<{ negative: true }> ? NegativeNumber<N> : number;
142
-
143
- type DefaultValueWhenNonNegativeIsOn<N extends number, R extends Constraints> =
144
- R extends Readonly<{ nonNegative: true }> ? NonNegativeNumber<N> : number;
145
-
146
- type DefaultValueWhenPositiveIsOn<N extends number, R extends Constraints> =
147
- R extends Readonly<{ positive: true }> ? PositiveNumber<N> : number;
79
+ C extends Constraints,
80
+ > = DefaultValueWhenNonZeroIsOn<N, C> &
81
+ DefaultValueWhenNegativeIsOn<N, C> &
82
+ DefaultValueWhenNonNegativeIsOn<N, C> &
83
+ DefaultValueWhenPositiveIsOn<N, C> &
84
+ DefaultValueWhenNonPositiveIsOn<N, C>;
85
+
86
+ type ConstraintsToResultType<C extends Constraints> =
87
+ ConstraintsToResultHelperType<ConstraintsToResultBrandKeys<C>>;
88
+
89
+ type ConstraintsToResultHelperType<
90
+ R extends Readonly<{ brandKeys: string; brandFalseKeys: string }>,
91
+ > =
92
+ IsNever<R> extends true
93
+ ? number
94
+ : Brand<number, R['brandKeys'], R['brandFalseKeys']>;
95
+
96
+ type ConstraintsToResultBrandKeys<C extends Constraints> =
97
+ | (C extends Readonly<{ int: true }>
98
+ ? Readonly<{ brandKeys: 'Int' | 'Finite'; brandFalseKeys: 'NaNValue' }>
99
+ : never)
100
+ | (C extends Readonly<{ finite: true }>
101
+ ? Readonly<{ brandKeys: 'Finite'; brandFalseKeys: 'NaNValue' }>
102
+ : never)
103
+ | (C extends Readonly<{ nonzero: true }>
104
+ ? Readonly<{ brandKeys: '!=0'; brandFalseKeys: 'NaNValue' }>
105
+ : never)
106
+ | (C extends Readonly<{ negative: true }>
107
+ ? Readonly<{
108
+ brandKeys: '!=0' | '< 2^15' | '< 2^16' | '< 2^31' | '< 2^32';
109
+ brandFalseKeys: '>=0' | 'NaNValue';
110
+ }>
111
+ : never)
112
+ | (C extends Readonly<{ positive: true }>
113
+ ? Readonly<{
114
+ brandKeys:
115
+ | '>=0'
116
+ | '!=0'
117
+ | '> -2^16'
118
+ | '> -2^32'
119
+ | '>= -2^15'
120
+ | '>= -2^31';
121
+ brandFalseKeys: 'NaNValue';
122
+ }>
123
+ : never)
124
+ | (C extends Readonly<{ nonNegative: true }>
125
+ ? Readonly<{
126
+ brandKeys: '>=0' | '> -2^16' | '> -2^32' | '>= -2^15' | '>= -2^31';
127
+ brandFalseKeys: 'NaNValue';
128
+ }>
129
+ : never)
130
+ | (C extends Readonly<{ nonPositive: true }>
131
+ ? Readonly<{
132
+ brandKeys: '< 2^15' | '< 2^16' | '< 2^31' | '< 2^32';
133
+ brandFalseKeys: '>=0' | 'NaNValue';
134
+ }>
135
+ : never);
136
+
137
+ type BrandKeysInt = ConstraintsToResultBrandKeys<Readonly<{ int: true }>>;
138
+
139
+ expectType<BrandKeysInt['brandKeys'], 'Finite' | 'Int'>('=');
140
+
141
+ expectType<BrandKeysInt['brandFalseKeys'], 'NaNValue'>('=');
142
+
143
+ type BrandKeysPositiveInt = ConstraintsToResultBrandKeys<
144
+ Readonly<{ int: true; positive: true }>
145
+ >;
148
146
 
149
- type DefaultValueWhenNonPositiveIsOn<N extends number, R extends Constraints> =
150
- R extends Readonly<{ nonPositive: true }> ? NonPositiveNumber<N> : number;
147
+ expectType<
148
+ BrandKeysPositiveInt['brandKeys'],
149
+ | 'Int'
150
+ | 'Finite'
151
+ | '!=0'
152
+ | '>=0'
153
+ | '> -2^16'
154
+ | '> -2^32'
155
+ | '>= -2^15'
156
+ | '>= -2^31'
157
+ >('=');
158
+
159
+ expectType<BrandKeysPositiveInt['brandFalseKeys'], 'NaNValue'>('=');
160
+
161
+ expectType<ConstraintsToResultType<Readonly<{ finite: true }>>, FiniteNumber>(
162
+ '=',
163
+ );
164
+
165
+ expectType<
166
+ ConstraintsToResultType<Readonly<{ finite: true; nonzero: true }>>,
167
+ NonZeroFiniteNumber
168
+ >('=');
169
+
170
+ expectType<
171
+ ConstraintsToResultType<Readonly<{ int: true; nonzero: true }>>,
172
+ NonZeroInt
173
+ >('=');
174
+
175
+ expectType<
176
+ ConstraintsToResultType<Readonly<{ int: true; positive: true }>>,
177
+ PositiveInt
178
+ >('=');
179
+
180
+ expectType<
181
+ ConstraintsToResultType<Readonly<{ int: true; negative: true }>>,
182
+ NegativeInt
183
+ >('=');
184
+
185
+ expectType<
186
+ ConstraintsToResultType<Readonly<{ int: true; nonNegative: true }>>,
187
+ Uint
188
+ >('=');
189
+
190
+ type DefaultValueWhenNonZeroIsOn<N extends number, C extends Constraints> =
191
+ C extends Readonly<{ nonzero: true }> ? NonZeroNumber<N> : number;
192
+
193
+ type DefaultValueWhenNegativeIsOn<N extends number, C extends Constraints> =
194
+ C extends Readonly<{ negative: true }> ? NegativeNumber<N> : number;
195
+
196
+ type DefaultValueWhenNonNegativeIsOn<N extends number, C extends Constraints> =
197
+ C extends Readonly<{ nonNegative: true }> ? NonNegativeNumber<N> : number;
198
+
199
+ type DefaultValueWhenPositiveIsOn<N extends number, C extends Constraints> =
200
+ C extends Readonly<{ positive: true }> ? PositiveNumber<N> : number;
201
+
202
+ type DefaultValueWhenNonPositiveIsOn<N extends number, C extends Constraints> =
203
+ C extends Readonly<{ nonPositive: true }> ? NonPositiveNumber<N> : number;
204
+
205
+ type NonZeroNumber<N extends number> = number extends N
206
+ ? number
207
+ : IsNonZero<N> extends true
208
+ ? N
209
+ : never;
151
210
 
152
211
  type NegativeNumber<N extends number> = number extends N
153
212
  ? number
@@ -173,6 +232,8 @@ type NonPositiveNumber<N extends number> = number extends N
173
232
  ? N
174
233
  : never;
175
234
 
235
+ type IsNonZero<N extends number> = N extends 0 ? false : true;
236
+
176
237
  type IsNegative<N extends number> = `${N}` extends `-${string}` ? true : false;
177
238
 
178
239
  type IsNonNegative<N extends number> =
@@ -185,57 +246,94 @@ type IsNonPositive<N extends number> =
185
246
 
186
247
  const createConstraintsPredicate =
187
248
  (constraints: Constraints) =>
188
- (value: number): boolean => {
189
- if (constraints.gt !== undefined && !(value > constraints.gt)) {
190
- return false;
249
+ (value: number): Result<true, string> => {
250
+ const {
251
+ gt,
252
+ gte,
253
+ lt,
254
+ lte,
255
+ min,
256
+ max,
257
+ multipleOf,
258
+ step,
259
+ finite,
260
+ int,
261
+ nonzero,
262
+ positive,
263
+ nonNegative,
264
+ negative,
265
+ nonPositive,
266
+ ..._rest
267
+ } = constraints;
268
+
269
+ expectType<keyof typeof _rest, never>('=');
270
+
271
+ if (gt !== undefined && !(value > gt)) {
272
+ return Result.err(errorMessage(value, 'gt', gt));
191
273
  }
192
274
 
193
- if (constraints.gte !== undefined && !(value >= constraints.gte)) {
194
- return false;
275
+ if (gte !== undefined && !(value >= gte)) {
276
+ return Result.err(errorMessage(value, 'gte', gte));
195
277
  }
196
278
 
197
- if (constraints.min !== undefined && !(value >= constraints.min)) {
198
- return false;
279
+ if (min !== undefined && !(value >= min)) {
280
+ return Result.err(errorMessage(value, 'min', min));
199
281
  }
200
282
 
201
- if (constraints.lt !== undefined && !(value < constraints.lt)) {
202
- return false;
283
+ if (lt !== undefined && !(value < lt)) {
284
+ return Result.err(errorMessage(value, 'lt', lt));
203
285
  }
204
286
 
205
- if (constraints.lte !== undefined && !(value <= constraints.lte)) {
206
- return false;
287
+ if (lte !== undefined && !(value <= lte)) {
288
+ return Result.err(errorMessage(value, 'lte', lte));
207
289
  }
208
290
 
209
- if (constraints.max !== undefined && !(value <= constraints.max)) {
210
- return false;
291
+ if (max !== undefined && !(value <= max)) {
292
+ return Result.err(errorMessage(value, 'max', max));
211
293
  }
212
294
 
213
- if (constraints.positive === true && !(value > 0)) {
214
- return false;
295
+ if (multipleOf !== undefined && value % multipleOf !== 0) {
296
+ return Result.err(errorMessage(value, 'multipleOf', multipleOf));
215
297
  }
216
298
 
217
- if (constraints.nonNegative === true && !(value >= 0)) {
218
- return false;
299
+ if (step !== undefined && value % step !== 0) {
300
+ return Result.err(errorMessage(value, 'step', step));
219
301
  }
220
302
 
221
- if (constraints.negative === true && !(value < 0)) {
222
- return false;
303
+ if (nonzero === true && !(value !== 0)) {
304
+ return Result.err(errorMessage(value, 'nonzero', true));
223
305
  }
224
306
 
225
- if (constraints.nonPositive === true && !(value <= 0)) {
226
- return false;
307
+ if (positive === true && !(value > 0)) {
308
+ return Result.err(errorMessage(value, 'positive', true));
227
309
  }
228
310
 
229
- if (
230
- constraints.multipleOf !== undefined &&
231
- value % constraints.multipleOf !== 0
232
- ) {
233
- return false;
311
+ if (nonNegative === true && !(value >= 0)) {
312
+ return Result.err(errorMessage(value, 'nonNegative', true));
234
313
  }
235
314
 
236
- if (constraints.step !== undefined && value % constraints.step !== 0) {
237
- return false;
315
+ if (negative === true && !(value < 0)) {
316
+ return Result.err(errorMessage(value, 'negative', true));
238
317
  }
239
318
 
240
- return true;
319
+ if (nonPositive === true && !(value <= 0)) {
320
+ return Result.err(errorMessage(value, 'nonPositive', true));
321
+ }
322
+
323
+ if (int === true && !Int.is(value)) {
324
+ return Result.err(errorMessage(value, 'int', true));
325
+ }
326
+
327
+ if (finite === true && !Number.isFinite(value)) {
328
+ return Result.err(errorMessage(value, 'finite', true));
329
+ }
330
+
331
+ return Result.ok(true);
241
332
  };
333
+
334
+ const errorMessage = (
335
+ value: number,
336
+ constraintName: string,
337
+ constraintValue: number | boolean,
338
+ ): string =>
339
+ `defaultValue [${value}] for number does not satisfy the constraint ${constraintName} = ${constraintValue}` as const;
@@ -1,4 +1,10 @@
1
1
  import { expectType, Result } from 'ts-data-forge';
2
+ import {
3
+ type NegativeInt,
4
+ type NegativeNumber,
5
+ type NonNegativeNumber,
6
+ type PositiveNumber,
7
+ } from 'ts-type-forge';
2
8
  import { type Type, type TypeOf } from '../type.mjs';
3
9
  import { number } from './number.mjs';
4
10
 
@@ -316,7 +322,7 @@ describe('number with constraints', () => {
316
322
  test('accepts valid default value', () => {
317
323
  const type = number(1, { positive: true });
318
324
 
319
- expectType<typeof type, Type<number>>('=');
325
+ expectType<typeof type, Type<PositiveNumber>>('=');
320
326
 
321
327
  assert.isTrue(type.is(10));
322
328
 
@@ -337,7 +343,7 @@ describe('number with constraints', () => {
337
343
  test('accepts valid default value', () => {
338
344
  const type = number(0, { nonNegative: true });
339
345
 
340
- expectType<typeof type, Type<number>>('=');
346
+ expectType<typeof type, Type<NonNegativeNumber>>('=');
341
347
 
342
348
  assert.isTrue(type.is(5));
343
349
 
@@ -358,7 +364,7 @@ describe('number with constraints', () => {
358
364
  test('accepts valid default value', () => {
359
365
  const type = number(-1, { negative: true });
360
366
 
361
- expectType<typeof type, Type<number>>('=');
367
+ expectType<typeof type, Type<NegativeNumber>>('=');
362
368
 
363
369
  assert.isTrue(type.is(-5));
364
370
 
@@ -379,7 +385,7 @@ describe('number with constraints', () => {
379
385
  test('accepts valid default value', () => {
380
386
  const type = number(0, { nonPositive: true });
381
387
 
382
- expectType<typeof type, Type<number>>('=');
388
+ expectType<typeof type, Type<NegativeInt>>('>=');
383
389
 
384
390
  assert.isTrue(type.is(-5));
385
391
 
@@ -471,7 +477,7 @@ describe('number with constraints', () => {
471
477
  test('accepts valid default value', () => {
472
478
  const type = number(6, { positive: true, multipleOf: 3 });
473
479
 
474
- expectType<typeof type, Type<number>>('=');
480
+ expectType<typeof type, Type<PositiveNumber>>('=');
475
481
 
476
482
  assert.isTrue(type.is(9));
477
483
 
@@ -497,7 +503,7 @@ describe('number with constraints', () => {
497
503
  step: 2,
498
504
  });
499
505
 
500
- expectType<typeof type, Type<number>>('=');
506
+ expectType<typeof type, Type<NonNegativeNumber>>('=');
501
507
 
502
508
  assert.isTrue(type.is(10));
503
509