Anthony Naddeo
Anthony Naddeo

Reputation: 2751

Why can't typescript infer an argument's type if it is a generic function using `keyof`?

Why does this yeild an error?

type MyType = {
    a: string, b: string
}


function cantInfer<In, Out>(fn: (i: In) => Out, i: In) {

}

function myFunction<K extends keyof MyType>(key: K): string {
    return '';
}

cantInfer(myFunction, 'a');

But this doesn't:

type MyType = {
    a: string, b: string
}


function cantInfer<In, Out>(fn: (i: In) => Out, i: In) {

}

function myFunction(key: keyof MyType): string {
    return '';
}

cantInfer(myFunction, 'a');

Note, the missing <K extends keyof MyType> in myFunction

Upvotes: 0

Views: 202

Answers (1)

w.brian
w.brian

Reputation: 17417

It should be noted that your first example does type-check when strictFunctionTypes is disabled. This disables bivariant parameter checking for function types. Without this check, the compiler allows code that may be unsound at runtime.

A good explanation can be found here:

... the question of whether a more-specific-type-accepting function should be assignable to a function accepting a less-specific type provides a prerequisite answer to whether an array of that more specific type should be assignable to an array of a less specific type. Having the latter not be the case would not be an acceptable type system in the vast majority of cases, so we have to take a correctness trade-off for the specific case of function argument types.

You fix the issue in your second example by making the type parameters for myFunction concrete: keyof MyType resolves to string at compile time, meaning the type signature for myFunction is effectively myFunction(key: string): string

The following example type-checks by doing the opposite—It makes the type constraints on canInfer less permissive (playground link):

type MyType = {
    a: string, b: string
}

function canInfer<In extends keyof MyType, Out>(fn: (i: In) => Out, i: In) {
    return fn(i);
}

function myFunction<K extends keyof MyType>(key: K): string {
    let myType = {
        a: "foo",
        b: "bar"
    }
    return myType[key];
}

alert(canInfer(myFunction, 'a'));

Upvotes: 2

Related Questions