Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Counterexample to current ReturnType logic with type parameters #56919

Open
jfet97 opened this issue Jan 1, 2024 · 1 comment Β· May be fixed by #55714
Open

Counterexample to current ReturnType logic with type parameters #56919

jfet97 opened this issue Jan 1, 2024 · 1 comment Β· May be fixed by #55714
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@jfet97
Copy link
Contributor

jfet97 commented Jan 1, 2024

πŸ”Ž Search Terms

"ReturnType inference any"

πŸ•— Version & Regression Information

This is the behavior in every version I tried

⏯ Playground Link

https://www.typescriptlang.org/play?#code/C4TwDgpgBAKgDFAvFAShYBXATgOxuCAHkJgD4AKYALlgEolSoBrCEAewDNZSBuAWABQAeiFQxAPQD8gwaEhQAogA9IAY2AQAJmky5885C3ZcMOJjjYB3HP2GiJkoA

πŸ’» 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.

@jfet97
Copy link
Contributor Author

jfet97 commented Jan 2, 2024

Just to say what's going on, given the fact the using a concrete type seems to a bad idea, the current attempt is to infer the arguments but discard them, and it seems promising.

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Jan 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants