Take the following function:
function test<T>(this: { value: T }) {
return this.value;
}
If you use call, apply or bind and provide the this context, TypeScript won't infer the provided type and it will return unknown in this case.
const a = test.apply({ value: 1 }); // a: unknown
const b = test.call({ value: 'b' }); // b: unknown
const fn = test.bind({ value: true }); // fn: () => unknown
Ok, it could be because apply, for example, is declared as apply<T, R>(this: (this: T) => R, thisArg: T): R, so it will require you to inform the R return type. But if you only explicitly declare the type of the test function, you will get the correct type:
const typed = (test<number>).apply({ value: 1 }); // typed: number
Why does it happen? Why it didn't infer R but then when I provided the type it did?
The bind/call/supply support introduced in TypeScript 3.2, as implemented in microsoft/TypeScript#27028, is not able to really deal with overloaded or generic functions properly. According to the documentation, "when using these methods on a generic function, type parameters will be substituted with [the unknown type]" (it says the empty object type {} but it's been unknown since TypeScript 3.5, as implemented in microsoft/TypeScript#30637). That explains the behavior you're seeing. There's an open feature request at microsoft/TypeScript#54707 for improved support here, but for now it's not part of the language.
As for why it works with (test<number>), that's an instantiation expression, where the specified type argument gets substituted into the generic function type parameter without calling the function. So test<number> is no longer a generic function:
function test<T>(this: { value: T }) {
return this.value;
}
type Test = typeof test // <T>(this: {value: T}) => T
const tn = test<number>;
type TestNumber = typeof tn // (this: { value: number; }) => number
It's just the specific function type (this: { value: number }) => number, for which bind/call/apply should work as desired.
Playground link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With