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
kis a parameter name, not a type. Instead you wantkeyto be generic in the type ofk. Like this. Does that meet your needs and I can write up an answer? Or am I missing something?