Preserve the homomorphism of inlined mapped types in declaration emit (#48091)

This commit is contained in:
Wesley Wigham
2022-03-09 10:31:54 -08:00
committed by GitHub
parent c70be8b501
commit ea4791d4d7
6 changed files with 118 additions and 3 deletions

View File

@@ -5079,10 +5079,16 @@ namespace ts {
const readonlyToken = type.declaration.readonlyToken ? factory.createToken(type.declaration.readonlyToken.kind) as ReadonlyKeyword | PlusToken | MinusToken : undefined;
const questionToken = type.declaration.questionToken ? factory.createToken(type.declaration.questionToken.kind) as QuestionToken | PlusToken | MinusToken : undefined;
let appropriateConstraintTypeNode: TypeNode;
let newTypeVariable: TypeReferenceNode | undefined;
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
// We have a { [P in keyof T]: X }
// We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType`
appropriateConstraintTypeNode = factory.createTypeOperatorNode(SyntaxKind.KeyOfKeyword, typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context));
if (!(getModifiersTypeFromMappedType(type).flags & TypeFlags.TypeParameter) && context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) {
const newParam = createTypeParameter(createSymbol(SymbolFlags.TypeParameter, "T" as __String));
const name = typeParameterToName(newParam, context);
newTypeVariable = factory.createTypeReferenceNode(name);
}
appropriateConstraintTypeNode = factory.createTypeOperatorNode(SyntaxKind.KeyOfKeyword, newTypeVariable || typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context));
}
else {
appropriateConstraintTypeNode = typeToTypeNodeHelper(getConstraintTypeFromMappedType(type), context);
@@ -5092,7 +5098,19 @@ namespace ts {
const templateTypeNode = typeToTypeNodeHelper(removeMissingType(getTemplateTypeFromMappedType(type), !!(getMappedTypeModifiers(type) & MappedTypeModifiers.IncludeOptional)), context);
const mappedTypeNode = factory.createMappedTypeNode(readonlyToken, typeParameterNode, nameTypeNode, questionToken, templateTypeNode, /*members*/ undefined);
context.approximateLength += 10;
return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine);
const result = setEmitFlags(mappedTypeNode, EmitFlags.SingleLine);
if (isMappedTypeWithKeyofConstraintDeclaration(type) && !(getModifiersTypeFromMappedType(type).flags & TypeFlags.TypeParameter) && context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) {
// homomorphic mapped type with a non-homomorphic naive inlining
// wrap it with a conditional like `SomeModifiersType extends infer U ? {..the mapped type...} : never` to ensure the resulting
// type stays homomorphic
return factory.createConditionalTypeNode(
typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context),
factory.createInferTypeNode(factory.createTypeParameterDeclaration(factory.cloneNode(newTypeVariable!.typeName) as Identifier)),
result,
factory.createKeywordTypeNode(SyntaxKind.NeverKeyword)
);
}
return result;
}
function createAnonymousTypeNode(type: ObjectType): TypeNode {