-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Return types of intersection of functions are incomplete and depend on order of declaration - an algorithm to fix it. #57095
Comments
Sounds like a duplicate of #57089. Why open another issue? You can just edit and open your other one. π€·ββοΈ
That still doesn't make sense to me. You have one function that always returns |
I don't see the value here, to be honest. The proposed algorithm introduces inconsistency between explicitly-written overloads and overloads formed from intersection, and breaks real code that people have certainly written where an earlier overload returns a more-specific type than the one it shadows. What do we get in return? It seems like we're trading one problem for another. |
Why "must" it be so? If it were intersection the return type of
would be never. That seems like proof by contradiction that the return type is not intersection. I don't see how we can make an exception for this one case, so it must apply to all cases. |
Like this:
In cases like the one above, the proposal would return Whereas current typescript returns For cases where the overloads are independent and not ordered, however, current algorithm chooses an arbitrary type that must be too narrow. Too narrow a type is much worse than too wide. |
Because it's the only logical thing to do. A union doesn't make sense and isn't type safe. And yes, the logical result is that the return type ends up being |
@MartinJohns - An
Here the argument Of course this is a psychotic corner case, because both overload orders have illogical shadowing, so it would never be implemented as overload, but instead as a single function. The algorithm is strong if it works on such corners case, which would happen if this proposal were used. Last, but not least, in example 1 of the original post, the current behavior is not
It is |
@MartinJohns
If, as you assert, both The point of this post is to claim that each case of |
These are already inconsistent (#56951), and
I'd rather some order-independent behavior for intersection, especially given that the order-dependent behavior currently provided doesn't match that of function overloads. |
I think this proposal as written is not tenable. The correct return type of overloads should be the intersection of the return types of applicable signatures. That is,
This means that you could still specify a most general overload, but get the benefits of more specific overloads. example 1: |
π Search Terms
#57002
#41874
intersection of functions, return type, incomplete, declaration order dependent
β Viability Checklist
β Suggestion
The return type of an intersection of functions should be at least complete and independent of the order of declaration of the functions.
π Motivating Example
Example 1: intersection of non-overload functions
Example 2: intersection of overload functions
Example 3: intersection of functions with object return types
Current algorithm:
g = g[0] & g[1] & ...
as though it were an ordered overload function{ g[0] ; g[1] ; ....}
.args
be the arguments.ReturnType<chooseOverload(g, args)>
The current algorithm treats
g
exactly as though it were an ordered overload function{ g[0] ; g[1] ; ....}
. Thereforeargs
can match at most one intersection memberg[i]
, resulting in an incomplete and declaration order dependent return type.Proposed algorithm # 1:
g = g[0] & g[1] & ...
be the intersection of functions.args
be the arguments.returnType = never
// initializeg[i]
ing
g[i]
is an overload functionreturnType = returnType | ReturnType<chooseOverload(g[i], args)>
args
extendsParameters<g[i]>
thenreturnType = returnType | ReturnType<g[i]>
returnType
This is expected to work for the above examples.
Proposed algorithm # 2:
Compared to the current algorithm, Proposed algorithm # 1 is a more expensive computation, but it is also complete and is not dependent on declaration order. If that computation is too expensive, then a simpler algorithm could be used:
i
ofReturnType<g[i]>
. Ifg[i]
is an overload function, it is calculated as the return type of the catch-all (a.k.a. cover) case for that overload sequence.Justification:
cover(g)
be the catch-all case (a.k.a. the cover) ofg
. i.e. for each parameter indexparamIndex
,Parameters<cover(g)>[paramIndex]
is the union overi
ofParameters<g[i]>[paramIdex]
, andReturnType<cover(g)>
is the union overi
ofReturnType<g[i]>
.cover(g)
is the smallest upper bound ofg
that can be represented with a single non-overload function.In the cases of examples 1 and 3, the answer wouldn't change, proposal # 2 is gives identical return type to that calculated with proposal # 1.
π» Use Cases
Getting accurate return types from intersections of functions.
When A and B are instances of a generic function, e.g.
then a workaround is to use the type
even though the type
AB
is wider than the intersection ofA
andB
.That workaround is similar in nature to using Proposed Algorithm # 2.
The text was updated successfully, but these errors were encountered: