Skip to content

Commit 0e26d18

Browse files
Add numeric range types (#319)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
1 parent 8028b63 commit 0e26d18

File tree

4 files changed

+194
-0
lines changed

4 files changed

+194
-0
lines changed

index.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ export {SetReturnType} from './source/set-return-type';
3737
export {Asyncify} from './source/asyncify';
3838
export {Simplify} from './source/simplify';
3939
export {Jsonify} from './source/jsonify';
40+
export {
41+
PositiveInfinity,
42+
NegativeInfinity,
43+
Finite,
44+
Integer,
45+
Negative,
46+
NonNegative,
47+
NegativeInteger,
48+
NonNegativeInteger,
49+
} from './source/numeric';
4050

4151
// Template literal types
4252
export {CamelCase} from './source/camel-case';

readme.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ Click the type names for complete docs.
128128
- [`Includes`](source/includes.d.ts) - Returns a boolean for whether the given array includes the given item.
129129
- [`Simplify`](source/simplify.d.ts) - Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability.
130130
- [`Jsonify`](source/jsonify.d.ts) - Transform a type to one that is assignable to the `JsonValue` type.
131+
- [`PositiveInfinity`](source/numeric-range.d.ts) - Matches the hidden `Infinity` type.
132+
- [`NegativeInfinity`](source/numeric-range.d.ts) - Matches the hidden `-Infinity` type.
133+
- [`Finite`](source/numeric-range.d.ts) - A finite `number`.
134+
- [`Integer`](source/numeric-range.d.ts) - A `number` that is an integer.
135+
- [`Negative`](source/numeric-range.d.ts) - A negative `number`/`bigint` (`-∞ < x < 0`)
136+
- [`NonNegative`](source/numeric-range.d.ts) - A non-negative `number`/`bigint` (`0 <= x < ∞`).
137+
- [`NegativeInteger](source/numeric-range.d.ts) - A negative (`-∞ < x < 0`) `number` that is an integer.
138+
- [`NonNegativeInteger](source/numeric-range.d.ts) - A non-negative (`0 <= x < ∞`) `number` that is an integer.
131139

132140
### Template literal types
133141

source/numeric.d.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
type Numeric = number | bigint;
2+
3+
type Zero = 0 | 0n;
4+
5+
/**
6+
Matches the hidden `Infinity` type.
7+
8+
Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/32277) if you want to have this type as a built-in in TypeScript.
9+
10+
@see NegativeInfinity
11+
12+
@category Basic
13+
*/
14+
// See https://github.com/microsoft/TypeScript/issues/31752
15+
// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
16+
export type PositiveInfinity = 1e999;
17+
18+
/**
19+
Matches the hidden `-Infinity` type.
20+
21+
Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/32277) if you want to have this type as a built-in in TypeScript.
22+
23+
@see PositiveInfinity
24+
25+
@category Basic
26+
*/
27+
// See https://github.com/microsoft/TypeScript/issues/31752
28+
// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
29+
export type NegativeInfinity = -1e999;
30+
31+
/**
32+
A finite `number`.
33+
You can't pass a `bigint` as they are already guaranteed to be finite.
34+
35+
Use-case: Validating and documenting parameters.
36+
37+
@example
38+
```
39+
import {Finite} from 'type-fest';
40+
41+
declare function setScore<T extends number>(length: Finite<T>): void;
42+
```
43+
44+
@category Utilities
45+
*/
46+
export type Finite<T extends number> = T extends PositiveInfinity | NegativeInfinity ? never : T;
47+
48+
/**
49+
A `number` that is an integer.
50+
You can't pass a `bigint` as they are already guaranteed to be integers.
51+
52+
Use-case: Validating and documenting parameters.
53+
54+
@example
55+
```
56+
import {Integer} from 'type-fest';
57+
58+
declare function setYear<T extends number>(length: Integer<T>): void;
59+
```
60+
61+
@see NegativeInteger
62+
@see NonNegativeInteger
63+
64+
@category Utilities
65+
*/
66+
// `${bigint}` is a type that matches a valid bigint literal without the `n` (ex. 1, 0b1, 0o1, 0x1)
67+
// Because T is a number and not a string we can effectively use this to filter out any numbers containing decimal points
68+
export type Integer<T extends number> = `${T}` extends `${bigint}` ? T : never;
69+
70+
/**
71+
A negative `number`/`bigint` (`-∞ < x < 0`)
72+
73+
Use-case: Validating and documenting parameters.
74+
75+
@see NegativeInteger
76+
@see NonNegative
77+
78+
@category Utilities
79+
*/
80+
export type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never;
81+
82+
/**
83+
A negative (`-∞ < x < 0`) `number` that is an integer.
84+
Equivalent to `Negative<Integer<T>>`.
85+
86+
You can't pass a `bigint` as they are already guaranteed to be integers, instead use `Negative<T>`.
87+
88+
Use-case: Validating and documenting parameters.
89+
90+
@see Negative
91+
@see Integer
92+
93+
@category Utilities
94+
*/
95+
export type NegativeInteger<T extends number> = Negative<Integer<T>>;
96+
97+
/**
98+
A non-negative `number`/`bigint` (`0 <= x < ∞`).
99+
100+
Use-case: Validating and documenting parameters.
101+
102+
@see NonNegativeInteger
103+
@see Negative
104+
105+
@example
106+
```
107+
import {NonNegative} from 'type-fest';
108+
109+
declare function setLength<T extends number>(length: NonNegative<T>): void;
110+
```
111+
112+
@category Utilities
113+
*/
114+
export type NonNegative<T extends Numeric> = T extends Zero ? T : Negative<T> extends never ? T : never;
115+
116+
/**
117+
A non-negative (`0 <= x < ∞`) `number` that is an integer.
118+
Equivalent to `NonNegative<Integer<T>>`.
119+
120+
You can't pass a `bigint` as they are already guaranteed to be integers, instead use `NonNegative<T>`.
121+
122+
Use-case: Validating and documenting parameters.
123+
124+
@see NonNegative
125+
@see Integer
126+
127+
@example
128+
```
129+
import {NonNegativeInteger} from 'type-fest';
130+
131+
declare function setLength<T extends number>(length: NonNegativeInteger<T>): void;
132+
```
133+
134+
@category Utilities
135+
*/
136+
export type NonNegativeInteger<T extends number> = NonNegative<Integer<T>>;

test-d/numeric.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {expectType} from 'tsd';
2+
import {Finite, Integer, Negative, NegativeInfinity, NegativeInteger, NonNegative, NonNegativeInteger, PositiveInfinity} from '../index';
3+
4+
// Finite
5+
declare const infinity: Finite<PositiveInfinity | NegativeInfinity>;
6+
declare const infinityMixed: Finite<1 | PositiveInfinity | NegativeInfinity>;
7+
8+
expectType<never>(infinity);
9+
expectType<1>(infinityMixed);
10+
11+
// Integer
12+
declare const integer: Integer<1>;
13+
declare const integerMixed: Integer<1 | 1.5>;
14+
declare const nonInteger: Integer<1.5>;
15+
declare const infinityInteger: Integer<PositiveInfinity | NegativeInfinity>;
16+
17+
expectType<1>(integer);
18+
expectType<never>(integerMixed); // This may be undesired behavior
19+
expectType<never>(nonInteger);
20+
expectType<never>(infinityInteger);
21+
22+
// Negative
23+
declare const negative: Negative<-1 | -1n | 0 | 0n | 1 | 1n>;
24+
25+
expectType<-1 | -1n>(negative);
26+
27+
// NegativeInteger
28+
declare const negativeInteger: NegativeInteger<-1 | 0 | 1>;
29+
30+
expectType<-1>(negativeInteger);
31+
32+
// NonNegative
33+
declare const nonNegative: NonNegative<-1 | -1n | 0 | 0n | 1 | 1n>;
34+
35+
expectType<0 | 0n | 1 | 1n>(nonNegative);
36+
37+
// NonNegativeInteger
38+
declare const nonNegativeInteger: NonNegativeInteger<-1 | 0 | 1>;
39+
40+
expectType<0 | 1>(nonNegativeInteger);

0 commit comments

Comments
 (0)