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. |
| 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.
| // 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.
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 Ta constraint that is the constraint of the template type on the check side of the conditional type - Give
infer Ta 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
: neverI 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.
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.
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.
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.
andrewbranch
left a comment
There was a problem hiding this comment.
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
Uis 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 Xlocation up to an appropriate constraint location instead; but doing so in general could be breaky in the community, since usinginferconstraints to break constraint relationships in our calculations is relatively common.