-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
In services, when overload resolution fails, create a union signature #19620
Conversation
04c7c91
to
8463d9e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@weswigham has spent considerable time thinking about unions of signatures.
- How close is this to what he's worked on?
- How close is his work to being merged into the main path code, instead of the error path?
@weswigham, care to comment on the previous two questions?
src/compiler/checker.ts
Outdated
// Normally we will combine overloads. Skip this if they have type parameters since that's hard to combine. | ||
// Don't do this if there is a `candidatesOutArray`, | ||
// because then we want the chosen best candidate to be one of the overloads, not a combination. | ||
if (!hasCandidatesOutArray && candidates.length > 1 && !candidates.some(c => !!c.typeParameters)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I think the pick-longest-signature code would benefit from being in a separate function as well.
- I think this condition would read better inverted in that case.
- If you don't do (1) and (2), I would recommend pushing the negation inside
some
and inverting it tocandidate.every(c => !c.typeParameters)
. ("every not" is easier to read as "none" than "not some")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea seems OK, but I'd like @weswigham to review as well, since he's thought about this a lot more.
src/compiler/checker.ts
Outdated
i < parameters.length ? parameters[i] : undefined); | ||
Debug.assert(symbols.length !== 0); | ||
const types = mapDefined(candidates, candidate => tryGetTypeAtPosition(candidate, i)); | ||
parameters.push(createCombinedSymbolWithType(symbols, getUnionType(types, /*subtypeReduction*/ true))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of creating a small-but-already-leaky abstraction, what about creating a single-use createCombinedSymbol that accounts for other common code, like the calls to [try]GetTypeOf[Parameter/Rest], mapDefined and getUnionType.
That function would not misrepresent itself in its name, and would make this function considerably more readable.
src/compiler/checker.ts
Outdated
thisParameter = createCombinedSymbolWithType(thisParameters, getUnionType(thisParameters.map(getTypeOfParameter), /*subtypeReduction*/ true)); | ||
} | ||
|
||
const { min: minArgumentCount, max: maxNonRestParam } = minAndMax(candidates, getNumNonRestParameters); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
linter should catch the double space? I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no such rule currently. Note that we do equals-sign alignment in a few places so we'd have to change our style to conform to such a rule -- it might be worth it though.
@sandersn #17819 Works well (if I remember, the only change it had were visible in the @andy-ms Would you be willing to check that the merged signature creation going on here functions the same as in #17819? It looks like you don't bother with setting the return type, and your |
@weswigham It looks like #17819 is doing a different thing than this -- that only modifies Since this is for a different use case I'm not actually sure what the correct behavior for using type parameters would be -- type inference failed the first time or we wouldn't get here, so if we synthesized type parameters for the union signature we would have to do type inference again, but that might fail again... I think it's best to avoid that case and just leave it as an error. We can't expect to get a good contextual type from a call to a generic function that fails anyway. declare function f(x: number): number;
declare function f(x: string): string;
f().toUpperCase(); You won't get an "extra" error at We can't accomplish this PR's goal by trying to create a new signature after the fact, because outside of the checker we don't know where a contextual type is coming from. |
811ae82
to
808b18b
Compare
808b18b
to
c8f70c4
Compare
43e67b9
to
e87fc88
Compare
Thanks for your contribution. This PR has not been updated in a while and cannot be automatically merged at the time being. For housekeeping purposes we are closing stale PRs. If you'd still like to continue working on this PR, please leave a message and one of the maintainers can reopen it. |
Fixes #19474
Edit: Fixes #19854 too
(
getUnionSignatures
seems like it would be useful but it returns aSignature[]
and I just want aSignature
.)