-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Fixes #30507 #32100
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
Fixes #30507 #32100
Conversation
Very excited to see this fix! Thanks for your work on this @timsuchanek! |
Sorry to bother, but maybe @ahejlsberg you can have a look as you introduced the |
Does it also fix #28470? |
@AviVahl from my understanding yes! I'll fix the new conflicts tomorrow, then it's again ready to get merged. I'll also add your example as a test case. |
4aaccf6
to
0c38f21
Compare
@AviVahl I added a fix so that your use-case with the constructor arguments is also supported and also added the 2 examples from #28470 as test cases. @weswigham @ahejlsberg can you prioritize this PR so that it doesn't get conflicts with |
@timsuchanek would you also be able to add a test for string literal completions too? declare function get<T, key: K extends keyof T>(obj: T, key: K): T[K];
get({ hello: 123, world: 456 }, "/**/"); This is one of the most-maddening and longest-standing issues we've had in completions. It would be really great to get this reviewed @ahejlsberg @weswigham @andrewbranch. |
@DanielRosenwasser I just simplified the "binary and" statement and also added your example as a test. Important to note: Your example also worked before my PR, I added it to the tests nonetheless. I didn't touch the code path for string literals, just the code path for object expressions when coming from the completion service. |
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.
Looks good to me, so long as others are happy with the public API changes. The failed builds are just API baseline issues after the latest merge with master.
d5667fa
to
ff6ceb5
Compare
Thanks @andrewbranch! Is there a way that I can accept the baselines and add a commit for that? |
Pass |
(also rebase/merge with master) |
I fixed the baseline merge. @weswigham @ahejlsberg any reservations about exporting |
Thanks a lot @andrewbranch. Can’t wait to see this merged! (This is a big deal for us at @prisma.) 🎉 |
@@ -2026,6 +2026,11 @@ declare namespace ts { | |||
*/ | |||
runWithCancellationToken<T>(token: CancellationToken, cb: (checker: TypeChecker) => T): T; | |||
} | |||
export enum ContextFlags { |
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 this as well as new signature need to be internal
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.
Do you mean we should use something different than the ContextFlags
when calling the code from the completion service to prevent this type being exposed?
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 Sheetal means to move ContextFlags
into one of the ts
namespaces that’s already marked /** @internal */
and then to mark the second parameter of TypeChecker['getContextualType']
as /* @internal */
(or declare it as an internal overload, but the linter might call that an unnecessary overload)
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.
Ok, makes sense! The overload option indeed makes the linter complain:
ERROR: /Users/tim/code/TypeScript/src/compiler/types.ts:3224:61 unified-signatures These overloads can be combined into one signature with an optional parameter.
The non-overload option, which I wrote like this:
getContextualType(
node: Expression,
/* @internal */ contextFlags?: ContextFlags
): Type | undefined;
spits out an invalid typescript.d.ts
:
getRootSymbols(symbol: Symbol): ReadonlyArray<Symbol>;
getContextualType(node: Expression,
: Type | undefined;
/**
* returns unknownSignature in the case of an error.
* returns undefined if the node is not valid.
* @param argumentCount Apparent number of arguments, passed in case of a possibly incomplete call. This should come from an ArgumentListInfo. See `signatureHelp.ts`.
*/
Is there a way to ignore the linter in this case?
Or what is the right way to add @internal
to a specific argument?
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.
Just filed #33350. I think it would be ok to ignore the line from the linter. @sheetalkamat?
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.
Yes in past we have marked the overload internal and disabled the rule for that line.
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.
Done. Had to adjust the tests because of #32266
Anything else you need from my side to get this merged?
@sheetalkamat @andrewbranch
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.
Cool, this doesn’t even touch the public API baselines now. Would be good to get an approval from another person on the team but I think this is good.
a118dc5
to
85ee123
Compare
85ee123
to
7883ec6
Compare
src/compiler/checker.ts
Outdated
@@ -16201,7 +16196,8 @@ namespace ts { | |||
const constraint = getConstraintOfTypeParameter(inference.typeParameter); | |||
if (constraint) { | |||
const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper); | |||
if (!inferredType || !context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { | |||
const isCompletionContext = contextFlags && (contextFlags & ContextFlags.Completion); | |||
if (!inferredType || isCompletionContext || !context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { |
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.
This doesn't seem like the right place to do this - rather than passing the flag all the way thru signature resolution, if this flag is set, we should just look at the "base signature" (getBaseSignature
) rather than starting inference at all, I think. Then again, if we did that we wouldn't get an instantiated constraint which reports more detailed information on circular constraints. Then again again, I'm pretty sure as-is this is caching into the resolvedSignature
of a node, which this very much is not - which means requesting completions poisons the cache with incorrect types for later checking.
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.
Thanks for the feedback @weswigham! Do you have a suggestion how we still could fix the autocompletion for this? This was, unfortunately, the only way I found (with my limited knowledge of the codebase).
Maybe introducing a new method as an alternative to getContextualType
just to be used by the completion service?
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 getContextualType
is still right, we just need to avoid using any resolved signature caches.
@typescript-bot pack this |
Hey @timsuchanek, I took a stab at addressing @weswigham’s concerns with some guidance from @DanielRosenwasser. I didn’t want to push a more substantial change here so I branched off of this PR and pushed a new one up at #33937. Would greatly appreciate your input there. Thanks! |
@timsuchanek, thanks for championing this through! Your commits here have been merged into master via #33937. 🏆 |
Thanks a lot, @andrewbranch for pushing this over the finish line! |
Fixes #30507
I saw, that when the object expression has incorrect properties, that don't exist on the type, the autocompletion works fine.
The decision, if the constraint type should be taken as the default or not is being made in checker.ts:15733 and as this is about 10 function calls deep when coming from https://github.com/microsoft/TypeScript/blob/master/src/services/completions.ts#L1457 I had to introduce a parameter in all these functions that can be passed down.
I saw, that there was already a
ContextFlags
type, which can be passed in togetContextualType
, so I extended it and to make it accessible from thecompletions.ts
, I had to add it to theTSChecker
types in https://github.com/microsoft/TypeScript/compare/master...timsuchanek:genericObjectExpressionCompletion?expand=1#diff-4b8bd1eea29904f1be39cd864e1a45c0R3335Let me know if I could have achieved this in a different way, but after many hours of debugging, this looked like the "most minimal" change to achieve the goal.
I added 2 tests, all tests are green.