From 260708d07ebf58218f5020e9452740c00ae90dbc Mon Sep 17 00:00:00 2001 From: Michael Arnaldi Date: Sat, 26 Mar 2022 00:41:59 +0000 Subject: [PATCH] bugfix: homomorphic mapped types when T is non-generic, solves 27995 --- src/compiler/checker.ts | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6c956a9037887..85bb467fd5ce2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15848,6 +15848,19 @@ namespace ts { // Eagerly resolve the constraint type which forces an error if the constraint type circularly // references itself through one or more type aliases. getConstraintTypeFromMappedType(type); + // Detect if the mapped type should be homomorphic to a tuple by checking the declaration of the constraint if it contains a keyof over a tuple + if (!node.nameType && node.typeParameter.constraint && isTypeOperatorNode(node.typeParameter.constraint) && node.typeParameter.constraint.operator === SyntaxKind.KeyOfKeyword) { + const keyOfTarget = getTypeFromTypeNode(node.typeParameter.constraint.type); + if (isTupleType(keyOfTarget)) { + // Instantiate the mapped type over a tuple with an identity mapper + const instantiatedTupleMappedType = instantiateMappedTupleType( + keyOfTarget, + type, + makeFunctionTypeMapper(identity) + ); + links.resolvedType = instantiatedTupleMappedType; + } + } } return links.resolvedType; } @@ -35579,14 +35592,17 @@ namespace ts { reportImplicitAny(node, anyType); } - const type = getTypeFromMappedTypeNode(node) as MappedType; - const nameType = getNameTypeFromMappedType(type); - if (nameType) { - checkTypeAssignableTo(nameType, keyofConstraintType, node.nameType); - } - else { - const constraintType = getConstraintTypeFromMappedType(type); - checkTypeAssignableTo(constraintType, keyofConstraintType, getEffectiveConstraintOfTypeParameter(node.typeParameter)); + const type = getTypeFromMappedTypeNode(node); + // Continue to check if the type returned is a mapped type, that means it wasn't resolved to a homomorphic tuple type + if (type.flags & TypeFlags.Object && (type as ObjectType).objectFlags & ObjectFlags.Mapped) { + const nameType = getNameTypeFromMappedType(type as MappedType); + if (nameType) { + checkTypeAssignableTo(nameType, keyofConstraintType, node.nameType); + } + else { + const constraintType = getConstraintTypeFromMappedType(type as MappedType); + checkTypeAssignableTo(constraintType, keyofConstraintType, getEffectiveConstraintOfTypeParameter(node.typeParameter)); + } } }