Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

access specific key of interface with parameter of function

basically speaking i have two interfaces:


interface config {
    lang: "en" | "de";
    test: number;
}

interface Accessor {
    key ( k: keyof config ): config[k];
}

and i want to dynamically have the type of whatever i access from the config type, via the Accessor object. is that possible?

note: my code is a lot more complicated and i cannot just access the keys directly, but it boils down to this in the end

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

The key() method signature in your Accessor interface should return an indexed access type of the form Config[X] where X is the type of the k parameter. Your attempt, Config[k], is not valid, because k is the value of the parameter; you need its type. This can be remedied using the typeof type operator like so:

interface Accessor {
    key(k: keyof Config): Config[typeof k];
}

This now compiles with no error. And you can even use it:

declare const a: Accessor;
const langProp = a.key("lang");
// const langProp: number | "en" | "de" 🤔
const testProp = a.key("test");
// const testProp: number | "en" | "de" 🤔

But as you can see, the behavior is not really what you want. The type of result is the union of possible property types of Config. In fact, the key() call signature returns the same type no matter what the input is. The type of k is just keyof Config. And keyof Config is the union "lang" | "test". And Config[typeof k] is Config[keyof Config] is Config["lang" | "test"] is Config["lang"] | Config["test"] is "en" | "de" | number. There’s nothing about the call signature which pays attention to which key is being passed in.


What you want is for a.key("lang") to return "en" | "de" and for a.key("test") to return number. That implies you want key() to be generic in the type of k. That is, instead of k being of type keyof Config, it should be of a generic type, let’s call it K, which is constrained to keyof Config:

interface Accessor {
    key<K extends keyof Config>(k: K): Config[typeof k];
}

Here K is a generic type parameter, declared inside angle brackets after the name of the method but before the opening parenthesis for the function parameter list. Once it is so declared, we can refer to K as a type elsewhere in the call signature.

And once we do that, everything works as desired:

declare const a: Accessor;
const langProp = a.key("lang");
// const langProp: "en" | "de" 👍
const testProp = a.key("test");
// const testProp: number 👍

Note that the typeof type operator isn’t really necessary if you already have a name for the type. In our case, k is of type K, so typeof k can just be written as K:

interface Accessor {
    key<K extends keyof Config>(k: K): Config[K];
}

And everything still works.

Playground link to code

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading