How to statically type function that returns array 1 to n

78 Views Asked by At

I'm trying to make a function that returns an array of numbers, from 0 to n, but I want the returned type to also be the known array statically.

const makeFoo = (n: number) => [...Array(n).keys()];
const foo1 = [0, 1, 2] as const; // readonly [0, 1, 2]
const foo2 = makeFoo(3); // number[]
console.log(foo1); // [0, 1, 2]
console.log(foo2); // [0, 1, 2]

While foo1 and foo2, have the same value at runtime, their types are different.

How do I define makeFoo's return type to be like foo1? (readonly [0, 1, ... n] where n is also known statically e.g. makeFoo = <T extends number>(n: T) => ...)

1

There are 1 best solutions below

1
On BEST ANSWER

You could use a recursive helper type which computes the return type based on a generic argument. However, you will need a type assertion for that (I don't think there's a way without) since [...Array(n).keys()] will always be inferred as number[].

type MakeFoo<N extends number, R extends any[] = []> = R["length"] extends N
  ? R
  : MakeFoo<N, [...R, R["length"]]>;

function makeFoo<N extends number>(n: N): MakeFoo<N> {
  return [...Array(n).keys()] as MakeFoo<N>
}

const foo3 = makeFoo(3);
//    ^? const foo3: [0, 1, 2]
const foo100 = makeFoo(100);
//    ^? const foo100: [0, 1, 2, 3, 4, 5, 6, 7, ... , 99]

TypeScript Playground


Caveat: This only works for a maximum of 999 items. Anything above will resolve to any.

const foo1000 = makeFoo(1000);
//              ~~~~~~~~~~~~~
// Type instantiation is excessively deep and possibly infinite.