Home > OS >  Typescript: Trying to setup a dynamic keyof typing
Typescript: Trying to setup a dynamic keyof typing

Time:01-15

I am trying to setup a typing where I want a specific type to be resolved from an object property:

interface SubjectA {
    id: string
    title: string
    description: string | null
    quantity: number
    links: string[]
}

let subjectA: SubjectA = {
    id: "abc",
    title: "def",
    description: null,
    quantity: 45,
    links: ["ghi", "jkl"],
}

executeProcessor(subjectA, {
    processors: [
        {
            id: "title",
            process: (should_be_infered_as_string) => {},
        },
        {
            id: "description",
            process: (should_be_infered_as_string_or_null) => {},
        },
        {
            id: "quantity",
            process: (should_be_infered_as_number) => {},
        },
        {
            id: "links",
            process: (should_be_infered_as_string_list) => {},
        },
    ],
})

Here, I'd like my parameter of process callback to be infered as the type of the property that match the given id.

Making the interface for the processor's item is not difficult:

interface PropertyProcessor<T, K extends keyof T> {
    id: K,
    process: (value: T[K]) => void
}

However, I'm struggling with typing the processors attribute itself:

interface ItemProcessor<T> {
    processors: PropertyProcessor<T, what_to_put_here_?>[]
}

So far, I've used keyof T as a replacement of what_to_put_here_?. But my parameters are now infered as any of the possible options (string | null | number | string[]).

I think my PropertyProcessor type should not have a generics K, and should determine itself the type of the process parameter:

interface PropertyProcessor<T> {
    id: keyof T,
    process: (value: T[same_as_id_type]) => void
}

But I don't known how to do this.

Thanks in advance for you're help.

Playground here

CodePudding user response:

You can get typescript to do this by adding a type parameter that will be inferred as a tuple of keys, and then mapping that tuple using a mapped type back to the array of processors:

type ProcessorMap<T, K extends Array<keyof T>> = {
    [P in keyof K]: PropertyProcessor<T, K[P] & keyof T>
}

function executeProcessor<T,P extends [keyof T] | (keyof T)[]>(item: T, processor: {
    processors: ProcessorMap<T, P>
}) {
}

Playground Link

The constraint [keyof T] | (keyof T)[] makes P be inferred to a tuple. Then we use the mapped type ProcessorMap to map back to the tuple we actually want.

  •  Tags:  
  • Related