Yeah, for whatever reason, tuple indices are numeric string literal types like "0"
and "1"
instead of the corresponding numeric literal types like 0
and 1
. There's also a number
index signature, so keyof ["a", "b"]
will give you number | "0" | "1" | ...
. Which means just using keyof
will let you assign any number whatsoever.
If you want to compute the numeric literals, you can use template literal types to do so, at least in TypeScript 4.8 and above:
type TupleIndices<T extends readonly any[]> =
Extract<keyof T, `${number}`> extends `${infer N extends number}` ? N : never;
First I Extract
the numeric string literal types from the full key set by filtering on `${number}`
, the set of all strings that can be parsed as a number. This eliminates number
itself (which isn't a string) and also "length"
and "slice"
and other array members. So that gives us "0" | "1" | "2" | ...
. Then I use the improved infer
types introduced in TypeScript 4.8 to convert those string literals to numeric literals.
That gives you this:
const arr = [1, 2] as const;
type ArrIndices = TupleIndices<typeof arr>;
// type ArrIndices = 0 | 1
type Also = TupleIndices<[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]>;
// type Also = 0 | 1 | 2 | 9 | 3 | 4 | 5 | 6 | 7 | 8
Playground link to code