Created
December 6, 2024 21:34
-
-
Save fredrare/1cacbacecbfdc2d992fa8c76f91f64d0 to your computer and use it in GitHub Desktop.
Small and simple generic number range implementation in TypeScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type NextDigit = [1, 2, 3, 4, 5, 6, 7, 8, 9] | |
type Split<Str> = Str extends `${infer Head extends number}${infer Tail}` ? [Head, ...Split<Tail>] : [] | |
type Increase<X extends number[]> = X extends [...infer Rest extends number[], infer Last extends number] ? | |
Last extends 9 ? [...Increase<Rest>, 0] : [...Rest, NextDigit[Last]] | |
: [1] | |
type TupleToString<T extends number[]> = | |
T extends [infer First extends number, ...infer Rest extends number[]] | |
? `${First}${TupleToString<Rest>}` | |
: "" | |
type NumberRange<Start extends number, End extends number> = Start extends End ? | |
End : | |
TupleToString<Increase<Split<`${Start}`>>> extends `${infer Num extends number}` ? | |
Start | NumberRange<Num, End> : never | |
// Works fine with small ranges | |
type Hour = NumberRange<0, 23> | |
// We can't use large ones though, because TypeScript throws an error. | |
// But we can bypass this by making a union out of the unions so that the recursion depth is smaller. | |
// This is far from ideal or elegant, so this is open for improvement. | |
type Minute = NumberRange<0, 29> | NumberRange<30, 59> | |
type Second = NumberRange<0, 29> | NumberRange<30, 59> | |
// This works just fine | |
const hours: Hour = 12 | |
// This doesn't because the operation returns a `number` | |
const minutes: Minute = 12 + 12 | |
// Unless we do this, of course | |
const seconds: Second = 12 + 12 as Second |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You can try this out in the playground. The result is a union type, so that's not ideal. Using it for small ranges should be just fine though.
I made this to kinda solve a use case for TypeScript #54925.