I have this interface for example:
interface MyInterface {
a: Something;
b: SomethingElse;
}
And let's say I call the following:
const object: MyInterface = {
a: {...},
b: {...},
}
const getValue = (key: keyof MyInterface) => {
const value: MyInterface[typeof key] = object[key];
return value;
}
const value = getValue('a');
What I end up having is value: Something | SomethingElse
instead of having value: Something
. Of course, this is a pseudo-example, in reality I'm parsing some data from the vuex getter inside a composable, but does anyone have a clear answer to this? Thank you.
I've tried accessing the interface by getting Interface[typeof key]
CodePudding user response:
According to https://www.typescriptlang.org/docs/handbook/2/keyof-types.html
The keyof operator takes an object type and produces a string or numeric literal union of its keys.
In this example, it makes sense that the keyof
operand combines the subfields of MyInterface
to result in Something | SomethingElse
. It is - as far as I understand - what the documentation states the operand does.
I have no experience with the keyof
, but to me this seems to do what it is designed for.
CodePudding user response:
The return type of getValue
function is manually set to MyInterface[typeof key]
. But even without this manual type, TypeScript infers it to be typeof object[key]
, i.e. MyInterface[keyof MyInterface]
, which is exactly the same, since typeof key
is keyof MyInterface
.
MyInterface[keyof MyInterface]
is an indexed access type, which produces a union of all property types accessed by the indexing type, here keyof MyInterface
. As described in patrickhrastnik's answer, the latter is itself a union of all keys of MyInterface
, i.e. 'a' | 'b'
. And MyInterface['a' | 'b']
is Something | SomethingElse
.
TypeScript performs only static type analysis, therefore this is the best it can conclude when all it knows is that the key
function parameter type is keyof MyInterface
.
Hence to get a narrower return type, we have to restrict the type of key
. Obviously, we still want to be able to call getValue
function with any key of MyInterface
, however.
A classic solution leverages generic function argument type inference to have TS guess the type of key
parameter where the function is called; there, it can infer it as a more specific type, instead of all the keyof
union. And we can still accept any of these keys as possible argument type by properly constraining the generic parameter type:
const getValue2 = <T extends keyof MyInterface>(key: T) => {
//^? (key: T) => MyInterface[T]
const value = object[key];
return value;
}
With this function using generics, TypeScript can infer a more specific return type for each call:
const value2 = getValue2('a');
// ^? Something
const value2b = getValue2('b');
// ^? SomethingElse