Skip to content

Commit d52d5e7

Browse files
authored
Refactor IsNumericLike and fix UnionMin, UnionMax types (#1192)
1 parent 642bb13 commit d52d5e7

File tree

5 files changed

+148
-13
lines changed

5 files changed

+148
-13
lines changed

source/internal/numeric.d.ts

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type {IsNever} from '../is-never.d.ts';
2-
import type {NegativeInfinity, PositiveInfinity} from '../numeric.d.ts';
2+
import type {Finite, NegativeInfinity, PositiveInfinity} from '../numeric.d.ts';
33
import type {UnknownArray} from '../unknown-array.d.ts';
44
import type {StringToNumber} from './string.d.ts';
5+
import type {IsAnyOrNever} from './type.d.ts';
56

67
/**
78
Returns the absolute value of a given value.
@@ -33,19 +34,20 @@ type A = IsNumberLike<'1'>;
3334
type B = IsNumberLike<'-1.1'>;
3435
//=> true
3536
36-
type C = IsNumberLike<1>;
37+
type C = IsNumberLike<'5e-20'>;
3738
//=> true
3839
39-
type D = IsNumberLike<'a'>;
40+
type D = IsNumberLike<1>;
41+
//=> true
42+
43+
type E = IsNumberLike<'a'>;
4044
//=> false
4145
*/
4246
export type IsNumberLike<N> =
43-
N extends number ? true
44-
: N extends `${number}`
47+
IsAnyOrNever<N> extends true ? N
48+
: N extends number | `${number}`
4549
? true
46-
: N extends `${number}.${number}`
47-
? true
48-
: false;
50+
: false;
4951

5052
/**
5153
Returns the minimum number in the given union of numbers.
@@ -54,11 +56,25 @@ Note: Just supports numbers from 0 to 999.
5456
5557
@example
5658
```
57-
type A = UnionMin<3 | 1 | 2>;
59+
type A = UnionMin<1 | 3 | 2>;
5860
//=> 1
61+
62+
type B = UnionMin<number>;
63+
//=> number
64+
65+
type C = UnionMin<any>;
66+
//=> any
67+
68+
type D = UnionMin<never>;
69+
//=> never
5970
```
6071
*/
61-
export type UnionMin<N extends number> = InternalUnionMin<N>;
72+
export type UnionMin<N extends number> =
73+
IsAnyOrNever<N> extends true ? N
74+
: number extends N ? number
75+
: NegativeInfinity extends N ? NegativeInfinity
76+
: [N] extends [PositiveInfinity] ? PositiveInfinity
77+
: InternalUnionMin<Finite<N>>;
6278

6379
/**
6480
The actual implementation of `UnionMin`. It's private because it has some arguments that don't need to be exposed.
@@ -77,17 +93,31 @@ Note: Just supports numbers from 0 to 999.
7793
```
7894
type A = UnionMax<1 | 3 | 2>;
7995
//=> 3
96+
97+
type B = UnionMax<number>;
98+
//=> number
99+
100+
type C = UnionMax<any>;
101+
//=> any
102+
103+
type D = UnionMax<never>;
104+
//=> never
80105
```
81106
*/
82-
export type UnionMax<N extends number> = InternalUnionMax<N>;
107+
export type UnionMax<N extends number> =
108+
IsAnyOrNever<N> extends true ? N
109+
: number extends N ? number
110+
: PositiveInfinity extends N ? PositiveInfinity
111+
: [N] extends [NegativeInfinity] ? NegativeInfinity
112+
: InternalUnionMax<Finite<N>>;
83113

84114
/**
85115
The actual implementation of `UnionMax`. It's private because it has some arguments that don't need to be exposed.
86116
*/
87117
type InternalUnionMax<N extends number, T extends UnknownArray = []> =
88118
IsNever<N> extends true
89119
? T['length']
90-
: T['length'] extends N
120+
: T['length'] extends N
91121
? InternalUnionMax<Exclude<N, T['length']>, T>
92122
: InternalUnionMax<N, [...T, unknown]>;
93123

source/internal/type.d.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,29 @@ type C = IfNotAnyOrNever<never, 'VALID', 'IS_ANY', 'IS_NEVER'>;
100100
export type IfNotAnyOrNever<T, IfNotAnyOrNever, IfAny = any, IfNever = never> =
101101
If<IsAny<T>, IfAny, If<IsNever<T>, IfNever, IfNotAnyOrNever>>;
102102

103-
/*
103+
/**
104+
Returns a boolean for whether the given type is `any` or `never`.
105+
106+
This types can be better to use than {@link IfNotAnyOrNever `IfNotAnyOrNever`} in recursive types because it does ***not*** evaluate any branches.
107+
108+
@example
109+
```
110+
// When `T` is a NOT `any` or `never` (like `string`) => Returns `false`
111+
type A = IsAnyOrNever<string>;
112+
//=> false
113+
114+
// When `T` is `any` => Returns `true`
115+
type B = IsAnyOrNever<any>;
116+
//=> true
117+
118+
// When `T` is `never` => Returns `true`
119+
type C = IsAnyOrNever<never>;
120+
//=> true
121+
```
122+
*/
123+
export type IsAnyOrNever<T> = IsNotFalse<IsAny<T> | IsNever<T>>;
124+
125+
/**
104126
Indicates the value of `exactOptionalPropertyTypes` compiler option.
105127
*/
106128
export type IsExactOptionalPropertyTypesEnabled = [(string | undefined)?] extends [string?]

test-d/internal/is-number-like.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,43 @@
11
import {expectType} from 'tsd';
22
import type {IsNumberLike} from '../../source/internal/numeric.d.ts';
3+
import type {NegativeInfinity, PositiveInfinity} from '../../index.d.ts';
34

5+
// Integer
6+
expectType<IsNumberLike<'-1'>>(true);
7+
expectType<IsNumberLike<-1>>(true);
8+
expectType<IsNumberLike<'+1'>>(true);
49
expectType<IsNumberLike<'1'>>(true);
510
expectType<IsNumberLike<1>>(true);
11+
12+
// Float
613
expectType<IsNumberLike<'-1.1'>>(true);
714
expectType<IsNumberLike<-1.1>>(true);
15+
expectType<IsNumberLike<'+1.1'>>(true);
16+
expectType<IsNumberLike<'1.1'>>(true);
17+
expectType<IsNumberLike<1.1>>(true);
18+
19+
// Sientific
20+
expectType<IsNumberLike<'+1.2e+3'>>(true);
21+
expectType<IsNumberLike<'1.2e+3'>>(true);
22+
expectType<IsNumberLike<1.2e+3>>(true);
23+
expectType<IsNumberLike<'+5e-3'>>(true);
24+
expectType<IsNumberLike<'5e-3'>>(true);
25+
expectType<IsNumberLike<5e-3>>(true);
26+
27+
expectType<IsNumberLike<'-1.2e+3'>>(true);
28+
expectType<IsNumberLike<-1.2e+3>>(true);
29+
expectType<IsNumberLike<'-5e-3'>>(true);
30+
expectType<IsNumberLike<-5e-3>>(true);
31+
32+
// Non valid numeric
833
expectType<IsNumberLike<'foo'>>(false);
34+
expectType<IsNumberLike<'1.2.3'>>(false);
35+
expectType<IsNumberLike<'5+1.2'>>(false);
36+
expectType<IsNumberLike<'5e-3.1'>>(false);
37+
38+
// Edge cases
39+
expectType<IsNumberLike<never>>({} as never);
40+
expectType<IsNumberLike<any>>({} as any);
41+
expectType<IsNumberLike<number>>(true);
42+
expectType<IsNumberLike<PositiveInfinity>>(true);
43+
expectType<IsNumberLike<NegativeInfinity>>(true);

test-d/internal/union-max.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {expectType} from 'tsd';
2+
import type {UnionMax} from '../../source/internal/numeric.d.ts';
3+
import type {NegativeInfinity, PositiveInfinity} from '../../index.d.ts';
4+
5+
expectType<UnionMax<1 | 3 | 2>>(3);
6+
expectType<UnionMax<10 | 5 | 2>>(10);
7+
expectType<UnionMax<0 | 5 | 2 | 1>>(5);
8+
expectType<UnionMax<1 | 2 | 5 | 3 | 7 | 9 | 0>>(9);
9+
10+
// TODO: push `negative-union-max-min` branch
11+
// Negatives are not supported yet (infinite).
12+
// expectType<UnionMax<-1 | -3 | -2>>(-1);
13+
// expectType<UnionMax<0 | -5 | -2>>(0);
14+
15+
// Edge cases
16+
expectType<UnionMax<any>>({} as any);
17+
expectType<UnionMax<never>>({} as never);
18+
expectType<UnionMax<number>>({} as number);
19+
expectType<UnionMax<(number & {})>>({} as number);
20+
expectType<UnionMax<(number & {}) | 1 | 5>>({} as number);
21+
expectType<UnionMax<PositiveInfinity>>({} as PositiveInfinity);
22+
expectType<UnionMax<NegativeInfinity>>({} as NegativeInfinity);
23+
expectType<UnionMax<1 | PositiveInfinity>>({} as PositiveInfinity);
24+
expectType<UnionMax<1 | NegativeInfinity>>({} as 1);

test-d/internal/union-min.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {expectType} from 'tsd';
2+
import type {UnionMin} from '../../source/internal/numeric.d.ts';
3+
import type {NegativeInfinity, PositiveInfinity} from '../../index.d.ts';
4+
5+
expectType<UnionMin<1 | 3 | 2>>(1);
6+
expectType<UnionMin<10 | 5 | 2>>(2);
7+
expectType<UnionMin<0 | 5 | 2 | 1>>(0);
8+
expectType<UnionMin<1 | 2 | 5 | 3 | 7 | 9 | 0>>(0);
9+
10+
// TODO: push `negative-union-max-min` branch
11+
// Negatives are not supported yet (skiped)
12+
// expectType<UnionMin<-1 | -3 | -2>>(-3);
13+
// expectType<UnionMin<0 | -5 | -2>>(-5);
14+
15+
// Edge cases
16+
expectType<UnionMin<any>>({} as any);
17+
expectType<UnionMin<never>>({} as never);
18+
expectType<UnionMin<number>>({} as number);
19+
expectType<UnionMin<(number & {})>>({} as number);
20+
expectType<UnionMin<(number & {}) | 1 | 5>>({} as number);
21+
expectType<UnionMin<PositiveInfinity>>({} as PositiveInfinity);
22+
expectType<UnionMin<NegativeInfinity>>({} as NegativeInfinity);
23+
expectType<UnionMin<1 | PositiveInfinity>>({} as 1);
24+
expectType<UnionMin<1 | NegativeInfinity>>({} as NegativeInfinity);

0 commit comments

Comments
 (0)