Is it possible to restrict number to a certain range

If You have small range, you can always write something like:

type MyRange = 5|6|7|8|9|10

let myVar:MyRange = 4; // oops, error :)

Of course it works just for integers and is ugly as hell :)


No it's not possible. That kind of precise type constraint is not available in typescript (yet?)

Only runtime checks/assertions can achieve that :(


Yes, it's possible BUT:

The 1st. Solution Will be a dirty Solution The 2nd. Solution Will be partial (from x to y where y is a small number, 43 in my case) The 3rd. Solution will be a Complete solution but really advance with Transformers, Decorators, etc.

1. Dirty solution ( the easiest and fast way first ) using @Adam-Szmyd solution:

type RangeType = 1 | 2 | 3

if you need an extensive range, just print and copy/paste:

// Easiest just incremental
let range = (max) => Array.from(Array(max).keys()).join(" | ");
console.log('Incremental')
console.log(range(20))
// With range and steps
let rangeS = (( min, max, step) => Array.from( new Array( max > min ? Math.ceil((max - min)/step) : Math.ceil((min - max)/step) ), ( x, i ) => max > min ? i*step + min : min - i*step ).join(" | "));
console.log('With range and steps')
console.log(rangeS(3,10,2))

You may be tented of doing things like this

const data = [1, 2, 4, 5, 6, 7] as const;
type P = typeof data[number];

showing that P has the enumerated types

but instead using functions

const rangeType20 = Array.from(Array(20).keys()) as const;

showing that can't be done with functions

But at the moment this doesn't work, only work if is a literal. Even the error is not quite correct.

2. Partial solution (source) Partial solution

type PrependNextNum<A extends Array<unknown>> = A['length'] extends infer T ? ((t: T, ...a: A) => void) extends ((...x: infer X) => void) ? X : never : never;

type EnumerateInternal<A extends Array<unknown>, N extends number> = { 0: A, 1: EnumerateInternal<PrependNextNum<A>, N> }[N extends A['length'] ? 0 : 1];

export type Enumerate<N extends number> = EnumerateInternal<[], N> extends (infer E)[] ? E : never;

export type Range<FROM extends number, TO extends number> = Exclude<Enumerate<TO>, Enumerate<FROM>>;

type E1 = Enumerate<43>;

type E2 = Enumerate<10>;

type R1 = Range<0, 5>;

type R2 = Range<0, 43>;

3. Complete solution but really advance with Transformers, Decorators, etc.

Using the functions on the first solution, you could replace at compiletime by the values, using transformer. Similarly, but on runtime using decorators.