Description
π Search Terms
"ReturnType inference any"
π Version & Regression Information
This is the behavior in every version I tried
β― Playground Link
π» Code
type T0 = ReturnType<<T>(t: T) => keyof T>; // any
type ExpectedReturnType = keyof unknown; // never
π Actual behavior
ReturnType
fails and returns any
π Expected behavior
Given the fact that the upper bound of the type parameter T
is unknown
, I'd expect keyof unknown
as result, i.e. never
.
Additional information about the issue
This is somewhat related to #55667 and it comes from the attempts made to understand why #55714 failed to pass all the test cases.
We have:
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
The problem seems to be located in compareSignaturesRelated
, called by getConditionalType
to resolve the ReturnType
's inner conditional type. We have that source = <T>(t: T): keyof T
is compared against target = (...args: any): never
. This never
is, in fact, the correct return type R
that was successfully inferred.
Because source
has a type parameter, instantiateSignatureInContextOf(source, target, ...)
comes into play and we get a new source
: (t: any): string | number | symbol
. I'm not 100% sure about the logic of instantiateSignatureInContextOf
, but it's worth noting that target
is the second argument of the call above and the type of target
's parameter is any
. Therefore it seems that, from the source
point of view, T
becomes just any
(and keyof any
is exactly string | number | symbol
).
We now have source = (t: any): string | number | symbol
to be compared against target = (...args: any): never
. This comparison fails because of the return types: string | number | symbol
is not assignable to never
, thus the assignability check also fails and we get the falseType
as result, i.e. any
.
Β
The example in this issue may seem intentionally crafted, and in fact it is, but the point was to highlight the same problem explained by this comment. There we have a similar issue with the source
we get from instantiateSignatureInContextOf
: any
has been substituted with never
inside ReturnType
definition, therefore if source
has a type parameter it will be instantiated with never
and sometimes things don't end well.
This probably undermines the possibility of having an all-encompassing definition for ReturnType
if we use a concrete type for the arguments. However you type the rest parameter inside it, I suppose you can always find a counterexample that uses a not-yet-instantiated type parameter to make the assignability check of the conditional fail.