Just a reminder that in your initial example, it does pass the type-check when strictFunctionTypes
is turned off. This essentially disables the bivariant parameter checking for function types. However, without this check, there's a possibility of allowing code that could result in runtime errors.
You can read more about this topic 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.
To rectify the issue in your second example, you need to make the type parameters for myFunction
explicit: keyof MyType
gets resolved to string
at compile time, resulting in the type signature for myFunction
being effectively myFunction(key: string): string
The following example demonstrates achieving type-check success by taking the opposite approach—by making the type constraints on canInfer
less lenient (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'));