-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Propagate outer type parameters of single signature types #57403
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
Conversation
…nd signature self-references
Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page. Also, please make sure @DanielRosenwasser and @RyanCavanaugh are aware of the changes, just as a heads up. |
@@ -34265,7 +34299,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |||
// If one or more arguments are still excluded (as indicated by CheckMode.SkipContextSensitive), | |||
// we obtain the regular type of any object literal arguments because we may not have inferred complete | |||
// parameter types yet and therefore excess property checks may yield false positives (see #17041). | |||
const checkArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType; | |||
const checkArgType = instantiateType(checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType, signature.mapper); |
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.
You'd think this'd be a noop, and ask questions like "How does an expression within a signature acquire a reference to a type parameter on the source signature?" or "Shouldn't a signature's mapper only need to be applied to the return type?" - and traditionally you'd be right. But at some point we started projecting the signature's type parameters onto expressions in the arguments contextually (and leaving them generic rather than immediately fixing the inference result), so now the arguments may also need the signature mapper applied to typecheck (usually they do not, but the linked issue is one such case where they do).
This instantiation is basically what makes it OK to type the inner expression in the linked issue as A[0]
, and get the argument to actually instantiate it to number
when it flows back into this signature and successfully typecheck.
// TODO: The signature may reference any outer inference contexts, but we map pop off and then apply new inference contexts, and thus get different inferred types. | ||
// That this is cached on the *first* such attempt is not currently an issue, since expression types *also* get cached on the first pass. If we ever properly speculate, though, | ||
// the cached "isolatedSignatureType" signature field absolutely needs to be included in the list of speculative caches. | ||
return getOrCreateTypeFromSignature(instantiateSignatureInContextOf(signature, contextualSignature, context), flatMap(inferenceContexts, c => c && map(c.inferences, i => i.typeParameter)).slice()); |
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 attaches all type variables being inferred in any active inference contexts as potential outer type parameters for this expression type - that allows them to be further instantiated later on (eg, when the signature those type parameters originally came from resolves and tries to map them into the inference results).
src/compiler/checker.ts
Outdated
// Inferring A to [A[0]] is a zero information inference (it guarantees A becomes its constraint), but oft arises from generic argument list inferences | ||
// By discarding it early, we can allow more fruitful results to be used instead. | ||
if (isTupleOfSelf(inference.typeParameter, candidate)) { | ||
return; |
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.
As the comment says, this just discards the bad inference so we can prefer the good one in the example in the linked issue. I think it's a good rule. Without it, we'd infer unknown
(from the constraint, since we'd prefer the contravariant [A[0]]
to [number]
), instead of number
.
type.symbol.declarations = [signature.declaration]; | ||
type.symbol.valueDeclaration = signature.declaration; | ||
} | ||
outerTypeParameters ||= signature.declaration && getOuterTypeParameters(signature.declaration, /*includeThisTypes*/ 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.
This gets the default outer type parameters syntactically, as is normal for most locations. The only exception is the inference context-affected case (which is also the interesting one for this issue), where we may affix type parameters to expressions from places not syntactically enclosing the signature.
@typescript-bot run dt |
Heya @weswigham, I've started to run the parallelized Definitely Typed test suite on this PR at f2f9df0. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the diff-based user code test suite on this PR at f2f9df0. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the public perf test suite on this PR at f2f9df0. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the diff-based top-repos suite on this PR at f2f9df0. You can monitor the build here. Update: The results are in! |
@weswigham Here are the results of running the user test suite comparing There were infrastructure failures potentially unrelated to your change:
Otherwise... Something interesting changed - please have a look. Details
|
@weswigham Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
Hey @weswigham, the results of running the DT tests are ready. |
@weswigham Here are the results of running the top-repos suite comparing Something interesting changed - please have a look. Details
|
@typescript-bot pack this |
Heya @weswigham, I've started to run the tarball bundle task on this PR at f2f9df0. You can monitor the build here. |
Hey @weswigham, I've packed this into an installable tgz. You can install it for testing by referencing it in your
and then running There is also a playground for this build and an npm module you can use via |
…xpressions with the context, rather than the signature mapper
@typescript-bot run dt |
Heya @weswigham, I've started to run the public perf test suite on this PR at 0822f33. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the diff-based user code test suite on this PR at 0822f33. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the parallelized Definitely Typed test suite on this PR at 0822f33. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the diff-based top-repos suite on this PR at 0822f33. You can monitor the build here. Update: The results are in! |
@weswigham Here are the results of running the user test suite comparing There were infrastructure failures potentially unrelated to your change:
Otherwise... Something interesting changed - please have a look. Details
|
@weswigham Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
Hey @weswigham, the results of running the DT tests are ready. |
@weswigham Here are the results of running the top-repos suite comparing Something interesting changed - please have a look. Details
|
*propagate. Sorry, I had to. |
@typescript-bot run dt |
Heya @weswigham, I've started to run the diff-based user code test suite on this PR at 17cbcca. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the diff-based top-repos suite on this PR at 17cbcca. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the parallelized Definitely Typed test suite on this PR at 17cbcca. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the public perf test suite on this PR at 17cbcca. You can monitor the build here. Update: The results are in! |
@weswigham Here are the results of running the user test suite comparing There were infrastructure failures potentially unrelated to your change:
Otherwise... Something interesting changed - please have a look. Details
|
Hey @weswigham, the results of running the DT tests are ready. |
@weswigham Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
@weswigham Here are the results of running the top-repos suite comparing Everything looks good! |
@typescript-bot pack this |
Starting jobs; this comment will be updated as builds start and complete.
|
Hey @weswigham, I've packed this into an installable tgz. You can install it for testing by referencing it in your
and then running There is also a playground for this build and an npm module you can use via |
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.
From what I remember from your previous explanation, this seemed good to go?
@@ -7088,7 +7089,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |||
|
|||
const abstractSignatures = filter(resolved.constructSignatures, signature => !!(signature.flags & SignatureFlags.Abstract)); | |||
if (some(abstractSignatures)) { | |||
const types = map(abstractSignatures, getOrCreateTypeFromSignature); | |||
const types = map(abstractSignatures, s => getOrCreateTypeFromSignature(s)); |
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.
Is this leftover from a previous refactor?
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.
Oh, it's just the second optional param not being assignable to map's param.
Fixes #55467
This is wildly more complicated than the issue would make you think, because it uncovers a few issues in how we construct "single signature types" and how that interacts with free type variable inference.
Specifically:
[A[0]]
forA
, since "the first element of A is the first element of A" is a tautological inference that adds no useful information, and always yieldsA
's constraint if selected. This improves inferences in scenarios involving a rest parameter alongside another inference site, as in the linked issue.