-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Allow calls to overloaded functions when all possible combinations of union type parameters resolve to valid overloads #17471
Comments
If you say a generic without any constraints, like declare function f(a: Promise<any>): number;
declare function f(a: any): string; |
Why is first overload ignored? If declare function f<T>(a: Promise<T>): number;
declare function f<T>(a: T): string;
const g = <T>(a: T) => f(a); // g is a simple proxy to f
declare const p: Promise<number>;
f(p); // first overload, number in compile-time, number in run-time
g(p); // g uses second overload of f in compile-time, so the result is of type string in compile-time
// in fact, it is number in run-time |
What is |
@kitsonk sorry, it was a typo. I've updated the sample code and added more explanation. |
Here you go! #1805. |
I was talking to @ahejlsberg about something like this a few days ago. Maybe we can bring it up again. |
@DanielRosenwasser that would be great. I've walked through referenced #1805, and it seemed to only cover mostly case 1, and completely missing case 2 where you cannot really control function application. |
Current real world example: the current typings for react mean that |
@tycho01 pointed that the issue was identified before in #12424 (comment) by @jcalz when discussing #6606 |
That is delaying overload resolution until generic parameters are bound, this is synthesizing a union of possible result types when overload resolution would otherwise fail, they don't seem that similar? |
Oh, reread OP, maybe it's worth opening what I thought it was as a new issue? |
Sorry for the spam: nope, it's just #1805 |
@simonbuchan my point for claiming they're related is that they have the same overlooked case where run-time behavior that expressed by overloads is not reflected in the type system respectfully. However I agree that it may be worth to distinguish due to different implementation paths. |
The generics at the repro in 12424 were unnecessary:
(I'm using #17961 though that wasn't needed) If I comment It appears the Edit: the type parameter's type seems to propagate through the Edit: So our call stack, from deep to shallow, goes like this:
The point to note there is how it loops from the So the question is how to pass this info in from the top For type arguments passed in from the top (here none, as The bigger question seems regular argument's types. I'd want to just similarly inject them, but it's no longer a 1:1 mapping to type parameters like it is for type arguments. Would anyone know how the type parameters are normally filled out in a call signature? In |
For me the problem is the way
The first option is not very useful for type level programming. The second option seems hard to implement and not in line with the current design. Edit: |
Hm. The way I looked at it I mean, I hope that means applying that type parameter info for the purpose of calculating the return type, by e.g. pre-filling them to use those rather than just the constraints, should do the trick. I may not seeing the full picture though, so any insights from those who do should help a lot. |
I think delaying the resolution is basically the same as propagating the overload. In both cases you'd have to compute the constraints to guarantee safety. In the propagation case you'd have the "overload" type at the top level, while in the other, you'd have to follow several indirections. I'm fine with either, as long as "all possible outcomes" (i.e. constraints) are properly computed and checked. Note: the lazy approach would need bounds on the type variable as is the case with |
Oh, my interpretation had been that propagating the overload would mean storing the different possible outcomes, which may sound manageable in this toy example, but perhaps less so if we'd be nesting calls with respectively I'm thinking we might not necessarily need much more complexity.
... then if rather than |
TypeScript Version: 2.4.1
Code
Case 1
Case 2
Expected behavior:
Case 1: compiles with no errors
Case 2: error that type
string | number
is not assignable to typestring
Actual behavior:
Case 1: error
Case 2: compiles without errors
Note
I remember here were some discussions about that, so that verifying all possible paths may result in N*M complexity (N overloads, M constituent types in unions). I could not find it.
The second case seems unsafe at all, because skips a possibly valid overload which may effect on return type. I expect that
f2(a)
would be of typenumber | string
because either of these two overloads can play. It actually has the same result witha: any
.The text was updated successfully, but these errors were encountered: