Home > Mobile >  Constrain type to keys of array but respect length
Constrain type to keys of array but respect length

Time:06-18

I have an array and I want to get a type which constrains its possible values to just valid keys. I thought that keyof typeof array would solve this, but it accepts all numbers.

const arr = ["a", "b", "c"] as const

type keyOfArr = keyof typeof arr // Does not work work. Accepts all numbers

let x: keyOfArr = 1    ✔️ Should work
let x: keyOfArr = 2    ✔️ Should work
let y: keyOfArr = -1   ❌ Should not work
let z: keyOfArr = 999  ❌ Should not work

CodePudding user response:

You need recursively iterate through the List and incrementaly add length to accumulator Result:

const arr = ["a", "b", "c"] as const

type List = (typeof arr)

type AllowedIndexes<T extends readonly any[], Result extends any[] = []> =
    T extends readonly [infer _, ...infer Rest]
    ? AllowedIndexes<Rest, [...Result, Result['length']]>
    : Result[number]


type Result = AllowedIndexes<List>

let x: Result = 1   // ok
let xx: Result = 2   // ok
let y: Result = -1  // error
let z: Result = 999 // error

Playground

Since TypeScript 4.8, it is doable without recursion:

const arr = ["a", "b", "c"] as const

type List = (typeof arr)

type ParseInt<T> = T extends `${infer Digit extends number}` ? Digit : never

type AllowedIndexes<T> =
    {
        [Prop in keyof Omit<T, number>]:
        (Prop extends `${number}`
            ? ParseInt<Prop> : never)
    }[keyof Omit<T, number>]

type Result = AllowedIndexes<List>

let x: Result = 1   // ok
let xx: Result = 2   // ok
let y: Result = -1  // error
let z: Result = 999 // error

Thanks to this PR, it is possible to infer numeric value from template string. It works only in TS nightly.

Playground

  • Related