With this method it can be possible to compare a type number range of 1,000,000 (possibly more)
I have expanded on this answer to add 3 more features:
1 - A condition that if you enter the same number twise then that number is returned
2 - The second number entered is also included in the range type map
3 - If used in certain way we can allow for typing number ranges between 0 - 89,999 (or even more)
The first 2 features are represented in this code:
type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N
? Acc[number]
: Enumerate<N, [...Acc, Acc['length']]>
type IntRange<F extends number, T extends number> = F extends T ?
F :
Exclude<Enumerate<T>, Enumerate<F>> extends never ?
never :
Exclude<Enumerate<T>, Enumerate<F>> | T
type G = IntRange<5,5>
// type G = 5
type T = IntRange<0, 9>
// type T = 0 | 9 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
Here's where things get interesting. Proceed only if you are happy with the end type being a union of string literals..
First we rewrite the name of "IntRange" and our "G" Range type variable names. Then make the renamed 'myRange' type a fixed value of multiple command substitutions:
type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N
? Acc[number]
: Enumerate<N, [...Acc, Acc['length']]>
type T<F extends number, T extends number> = F extends T ?
F :
Exclude<Enumerate<T>, Enumerate<F>> extends never ?
never :
Exclude<Enumerate<T>, Enumerate<F>> | T
type myRange = `${T<0, 8>}${T<0, 9>}${T<0, 9>}${T<0, 9>}${T<0, 9>}`
Here myRange is a union of string literals between 00,000 - 89,999
(any number string between these values with work, any outside will not)
We can start doing some more funky stuff if we abstract all of this in a tidy little interface:
interface R<N extends number = 0, T extends number = 0, Acc extends number[] = []> {
O: {
H: Acc['length'] extends N
? Acc[number]
: R<N, 0, [...Acc, Acc['length']]>["O"]["H"]
},
T: N extends T ? N : Exclude<R<T>["O"]["H"], R<N>["O"]["H"]> extends never ? never : Exclude<R<T>["O"]["H"], R<N>["O"]["H"]> | T ,
K: {
XX1 : `${R<0,8>["T"]}${R<0,9>["T"]}${R<0,9>["T"]}${R<0,9>["T"]}`,
}
U: R["K"][keyof R["K"]]
}
type myRange = R["U"]
Here myRange is also a union of string literals between 00,000 - 89,999 However we can keep adding properties to the R.K object and the value of those keys will be added to the union. With this we can easily increase the number range from 89,999 to 99,999 like this:
interface R<N extends number = 0, T extends number = 0, Acc extends number[] = []> {
O: {
H: Acc['length'] extends N
? Acc[number]
: R<N, 0, [...Acc, Acc['length']]>["O"]["H"]
},
T: N extends T ? N : Exclude<R<T>["O"]["H"], R<N>["O"]["H"]> extends never ? never : Exclude<R<T>["O"]["H"], R<N>["O"]["H"]> | T ,
K: {
XX1 : `${R<0, 8>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
XX2 : `${R<9, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`
}
U: R["K"][keyof R["K"]]
}
type myRange = R["U"]
The limit of any one string literal range type seems to be 89,999. If you go over this it still compiles the type however the type becomes 'any'. When you chain them and merge the union types like above however it appears this limit can be bypassed.
I haven't tested the limit of this but here's one for 000,000 - 999,999. Although its starting to make a noticeable impact on the performance of the type compiler..
The Compiler makes it look like the limit is 888,888 but it is 999,999 TS Fiddle
interface R<N extends number = 0, T extends number = 0, Acc extends number[] = []> {
O: {
H: Acc['length'] extends N
? Acc[number]
: R<N, 0, [...Acc, Acc['length']]>["O"]["H"]
},
T: N extends T ? N : Exclude<R<T>["O"]["H"], R<N>["O"]["H"]> extends never ? never : Exclude<R<T>["O"]["H"], R<N>["O"]["H"]> | T ,
K: {
xx1 : `${R<0, 1>["T"]}${R<0, 3>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
xx2 : `${R<0, 1>["T"]}${R<4, 7>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
XX3 : `${R<0, 1>["T"]}${R<8, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
XX4 : `${R<2, 3>["T"]}${R<0, 3>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
XX5 : `${R<2, 3>["T"]}${R<4, 7>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
XX6 : `${R<2, 3>["T"]}${R<8, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
XX7 : `${R<4, 5>["T"]}${R<0, 3>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
XX8 : `${R<4, 5>["T"]}${R<4, 7>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
XX9 : `${R<4, 5>["T"]}${R<8, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
X10 : `${R<6, 7>["T"]}${R<0, 3>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
X11 : `${R<6, 7>["T"]}${R<4, 7>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
X12 : `${R<6, 7>["T"]}${R<8, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
X13 : `${R<8, 9>["T"]}${R<0, 3>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
X14 : `${R<8, 9>["T"]}${R<4, 7>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
X15 : `${R<8, 9>["T"]}${R<8, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}${R<0, 9>["T"]}`,
}
U: R["K"][keyof R["K"]]
}
type myRange = R["U"]
var myString: myRange = "999999"