1

I am looking to get type inference based on the value of keys in an array.

For context, I'm working on a class where you can pass in a list of options and a default value:

interface Option {
    value: string;
}

interface SelectArgs {
    options: Option[];
    defaultValue: Option['value'];
}

class SelectConfig {
    options: Option[];
    defaultValue: Option['value'] // can I get the value options with autocompletion here?

    constructor(args: SelectArgs ) {
        Object.assign(this, args);
    }

}

I would be using this in the context of a class, so for example, if I instantiate a new class:

const select = new SelectOptions({
    options: [{value: 'Hello'}, {value: 'World'}]
    defaultValue: "Hello" // I want to give me type inference of either "Hello" | "World"

})

What is the best way to type defaultValue in this case so it is always one of the passed in values of options?

1 Answer 1

3

Yes, it'll look something like this, using a generic parameter for the class:

type Narrow<T> =
    | (T extends infer U ? U : never)
    | Extract<T, number | string | boolean | bigint | symbol | null | undefined | []>
    | ([T] extends [[]] ? [] : { [K in keyof T]: Narrow<T[K]> });

class SelectConfig<Options extends Option[]> {
    options!: Options;
    defaultValue!: Options[number]['value'] 

    constructor(args: {
        options: Narrow<Options>;
        defaultValue: Options[number]["value"]
    }) {
        Object.assign(this, args);
    }
}

I opted to use this Narrow utility type so you don't need to use as const when creating the class. We'll narrow the type of the given options, then set the type of the default value to any of the values given in the options.

Playground

Sign up to request clarification or add additional context in comments.

6 Comments

The instance members should be using the generic Options here, not Option.
Nice catch, for some reason I was concerned that later on when you used the members it would give you type errors because the members were too specific (adding options, etc), but it looks like it doesn't.
Nice. Didn't know yet about this Narrow utility. Where did you find it?
@ViktorLuft I found it in the TypeScript community Discord server. It's very commonly used, so much so that their Discord bot has a command to bring this type up.
Also, as const is probably fine, and arguably a lot simpler. Regardless, nice answer, up voted!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.