Home > Blockchain >  Typescript: Infer type by passing function parameter to a Interface
Typescript: Infer type by passing function parameter to a Interface

Time:12-14

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

Playground Link

  • Related