-
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
fix(38388) Add support for this
parameters in iterationTypes and fix caching issues
#41451
Conversation
The TypeScript team hasn't accepted the linked issue #38388. If you can get it accepted, this PR will have a better chance of being reviewed. |
I noticed on my machine that the errors in |
@@ -1,5 +1,4 @@ | |||
tests/cases/compiler/errorsInGenericTypeReference.ts(11,17): error TS2304: Cannot find name 'V'. | |||
tests/cases/compiler/errorsInGenericTypeReference.ts(17,31): error TS2304: Cannot find name 'V'. |
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 is probably not good. I am still pretty sure this has nothing to do with my changes, I just wanted to update the baseline references so my tests don't fail. Perhaps another issue should be opened for this case?
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 a lot going on here and it will take me some time to review. I'm not certain I agree with some of the changes here, as there are a large number of places where you've switched to use a less precise type so that you can treat node
as only an "error node". A separate errorNode
argument argument would likely be preferable in those cases.
It might be worth breaking out the changes to fix the caching issues you mentioned into a separate PR, as those changes seem less controversial to me.
src/compiler/checker.ts
Outdated
@@ -26267,33 +26268,33 @@ namespace ts { | |||
return true; | |||
} | |||
|
|||
function callLikeExpressionMayHaveTypeArguments(node: CallLikeExpression): node is CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement { | |||
function callLikeExpressionMayHaveTypeArguments(node: Node): node is CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement { |
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.
Why was the precision for this argument's type reduced?
src/compiler/checker.ts
Outdated
// This gets us diagnostics for the type arguments and marks them as referenced. | ||
forEach(node.typeArguments, checkSourceElement); | ||
} | ||
function resolveUntypedCall(node: Node): Signature { |
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.
Why was the precision for this argument's type reduced?
src/compiler/checker.ts
Outdated
checkExpression(argument); | ||
}); | ||
} | ||
return anySignature; | ||
} | ||
|
||
function resolveErrorCall(node: CallLikeExpression): Signature { | ||
resolveUntypedCall(node); | ||
function resolveErrorCall(node?: Node): Signature { |
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'm still not clear why you're using a less specific type here.
src/compiler/checker.ts
Outdated
@@ -26367,58 +26368,68 @@ namespace ts { | |||
return !!(t.flags & (TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Unknown | TypeFlags.Any)); | |||
} | |||
|
|||
function hasCorrectArity(node: CallLikeExpression, args: readonly Expression[], signature: Signature, signatureHelpTrailingComma = false) { | |||
function hasCorrectArity(node: Node | undefined, args: readonly Expression[], signature: Signature, signatureHelpTrailingComma: boolean, implicitThisType?: Type) { |
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.
Again, I don't agree with using a less precise type here.
src/compiler/checker.ts
Outdated
const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper); | ||
inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType); | ||
context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined; | ||
function inferTypeArguments(node: Node | undefined, signature: Signature, args: readonly Expression[], checkMode: CheckMode, context: InferenceContext, implicitThisType?: Type): Type[] { |
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.
Another case of using a less precise type, and I'm still not clear on why implicitThisType
should cause most of this function to be ignored.
Thanks for getting back to me! I really wasn't sure whether to prioritize making fewer changes and making it work with what's already there or to make changes like you suggested. I tried to think of how to solve the problem while making the smallest number of changes, then take one step towards what I would consider ideal. I wasn't sure what kind of philosophy there is about these kinds of things so I just guessed. Since my guess was wrong, I'll go back and make the changes I think would be ideal instead of trying to guess where my hands should be tied. I did kinda deviate from that anyway and got caught up a bit when I noticed a bunch of other issues while looking at the code. I will work on this more this week. I'll make this better. (My first implementation was more along the lines of your suggestion but I thought it could be rejected for adding more than absolutely necessary) |
🧹 Closing due to staleness. Please open a new PR if you'd like to continue. Thanks! |
Fixes #38388
Fixes #39693
This PR:
this
parameters in iterationTypes!getIterationTypesOfIterable
had 3 problems:getIterationTypesOfIterableSlow
get called every time and produce the proper errors.getIterationTypesOfIterable
whether something was cached to determine whether or not to cache orreportTypeNotIterableError
(which we don't want to do if there was already an error fromresolveCall
, for example).implicitThisType === type
.use
permits either AsyncIterables or (Sync)Iterables converted viagetAsyncFromSyncIterationTypes
it would call these functions in this order:getIterationTypesOfIterableCached(type, asyncResolver) || getIterationTypesOfIterableFast(type, asyncResolver) || getIterationTypesOfIterableCached(type, syncResolver) || getIterationTypesOfIterableFast(type, syncResolver) || getIterationTypesOfIterableSlow(type, asyncResolver) || getIterationTypesOfIterableSlow(type, syncResolver)
. This is incorrect because an object could have anSymbol.asyncIterator
that is neither cached nor an instance of a simple global type, and that logic could then fall back on thesyncResolver
section. Now it runscached || fast || slow
in order for bothSymbol.asyncIterator
andSymbol.iterator
when applicable. This led to the 2nd and 3rd bugs demonstrated in the playground link above.iterationTypesOfAsyncIterable
was shared betweenSymbol.iterator
andSymbol.asyncIterator
. NowiterationTypesOfAsyncedSyncIterable
is used to storeSymbol.iterator
calls that got converted viagetAsyncFromSyncIterationTypes
.To make implicit
Symbol.iterator
calls like actualSymbol.iterator
calls, I hooked them up to theresolveCall
function. This function and a few of the ones it calls had to be modified so that arbitraryNode
s could be passed into it without analyzing theNode
as whatever it is. The implicit call case is differentiated by theimplicitThisType
being passed around. I.e. whenimplicitThisType
is present, then the node should be treated solely as anerrorNode
, and the only NodeKind-specific behavior should be in getting the boundaries for error messages.typeArguments
and parametricargs
should always be empty.Implicit iterator calls do have different behavior from regular calls though. Intersected iterator functions will produce intersected results instead of just grabbing the first one. See #39693. Unioned iterator functions will evaluate each unioned constituent separately (passing the constituent, not the top-level type, as the
implicitThisType
) and the results will be unioned together. See here for examples. This means there will be inconsistency between howfor (const x of obj)
vsfor (const x of obj[Symbol.iterator]())
is evaluated, although there was already inconsistency before and I think it is for the better.