-
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
Add infer T
constraint inference rule matching up mapped type templates across check/extends types
#43649
Add infer T
constraint inference rule matching up mapped type templates across check/extends types
#43649
Conversation
…ates across check/extends types
@typescript-bot user test this |
Heya @weswigham, I've started to run the tarball bundle task on this PR at 6852a3c. You can monitor the build here. |
Heya @weswigham, I've started to run the parallelized community code test suite on this PR at 6852a3c. You can monitor the build here. |
Heya @weswigham, I've started to run the parallelized Definitely Typed test suite on this PR at 6852a3c. You can monitor the build here. |
Heya @weswigham, I've started to run the perf test suite on this PR at 6852a3c. You can monitor the build here. Update: The results are in! |
Heya @weswigham, I've started to run the extended test suite on this PR at 6852a3c. 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 |
@weswigham Here they are:Comparison Report - master..43649
System
Hosts
Scenarios
Developer Information: |
The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master. |
@@ -12585,6 +12585,19 @@ namespace ts { | |||
else if (grandParent.kind === SyntaxKind.TypeParameter && grandParent.parent.kind === SyntaxKind.MappedType) { | |||
inferences = append(inferences, keyofConstraintType); | |||
} | |||
// When an 'infer T' declaration is the template of a mapped type, and that mapped type if the extends |
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.
// When an 'infer T' declaration is the template of a mapped type, and that mapped type if the extends | |
// When an 'infer T' declaration is the template of a mapped type, and that mapped type is the extends |
// clause of a conditional whose check type is also a mapped type, give it the constraint of the template | ||
// of the check type's mapped 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.
give it the constraint of the template of the check type's mapped type
I think this phrasing is ambiguous, and I want to nitpick it just to figure out whether I’m understanding the goal. It could be interpreted in one of two ways:
- Give
infer T
a constraint that is the constraint of the template type on the check side of the conditional type - Give
infer T
a constraint that is the template type on the check side of the conditional type
Reading the code and the test case, I think what you mean is (2), because given
type KeysWithoutStringIndex<T> =
{ [K in keyof T]: string extends K ? never : K } extends { [_ in keyof T]: infer U }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ “template of the check type’s mapped type”
? U
: never
I don’t know what it means for string extends K ? never : K
to have a constraint in this case, and it looks like the type you’re appending to inferences
is just the type of string extends K ? never : K
instantiated with K
→ keyof T
. Did I understand that right?
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 on both fronts. That constraint will then later be executed and become a single type.
const checkMappedType = (grandParent.parent as ConditionalTypeNode).checkType as MappedTypeNode; | ||
const nodeType = getTypeFromTypeNode(checkMappedType.type!); | ||
inferences = append(inferences, instantiateType(nodeType, | ||
makeUnaryTypeMapper(getDeclaredTypeOfTypeParameter(getSymbolOfNode(checkMappedType.typeParameter)), checkMappedType.typeParameter.constraint ? getTypeFromTypeNode(checkMappedType.typeParameter.constraint) : keyofConstraintType) |
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.
What would it mean for checkMappedType.typeParameter.constraint
to be undefined
? Is that only an error condition for circular constraints?
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.
So, we issue an error on it, but it's when you wrote a mapped type like {[K in]: K}
- the constraint is simply missing. That's just handled gracefully here, rather than crashing.
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.
Clarifying the comment some would be helpful, but the change looks good.
Fixes #43357
The example in the issue used to work, I assume, due to some flawed conditional type assignability (or instantiation/constraint) rules we used to have. Inherently, there's no reason it should have worked, since
U
is unconstrained. By adding a constraint inference site for this example, we can get it to succeed now that we have proper rules for relating conditionals in place. More generally, if we wanted to move from syntactic matching for this case (and broaden how applicable this fix is), we could do inference between the source and target sides of the containing uninstantiated conditional, and rely on inference to match theinfer X
location up to an appropriate constraint location instead; but doing so in general could be breaky in the community, since usinginfer
constraints to break constraint relationships in our calculations is relatively common.