From b61d4858d43c2c0241543653197d9ff53bfdb9b7 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 23 May 2018 16:01:52 -0700 Subject: [PATCH] Simplify convertToMappedObjectType (#24360) --- .../codefixes/convertToMappedObjectType.ts | 92 ++++++------------- 1 file changed, 27 insertions(+), 65 deletions(-) diff --git a/src/services/codefixes/convertToMappedObjectType.ts b/src/services/codefixes/convertToMappedObjectType.ts index b8315ce6d96..ff86a9872bb 100644 --- a/src/services/codefixes/convertToMappedObjectType.ts +++ b/src/services/codefixes/convertToMappedObjectType.ts @@ -6,89 +6,51 @@ namespace ts.codefix { type FixableDeclaration = InterfaceDeclaration | TypeAliasDeclaration; - interface Info { - indexSignature: IndexSignatureDeclaration; - container: FixableDeclaration; - otherMembers: ReadonlyArray; - parameterName: Identifier; - parameterType: TypeNode; - } - registerCodeFix({ errorCodes, getCodeActions: context => { const { sourceFile, span } = context; - const info = getFixableSignatureAtPosition(sourceFile, span.start); - if (!info) return; - const { indexSignature, container, otherMembers, parameterName, parameterType } = info; - - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, indexSignature, container, otherMembers, parameterName, parameterType)); - return [createCodeFixAction(fixId, changes, [Diagnostics.Convert_0_to_mapped_object_type, idText(container.name)], fixId, [Diagnostics.Convert_0_to_mapped_object_type, idText(container.name)])]; + const info = getInfo(sourceFile, span.start); + if (!info) return undefined; + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info)); + const name = idText(info.container.name); + return [createCodeFixAction(fixId, changes, [Diagnostics.Convert_0_to_mapped_object_type, name], fixId, [Diagnostics.Convert_0_to_mapped_object_type, name])]; }, fixIds: [fixId], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { - const info = getFixableSignatureAtPosition(diag.file, diag.start); - if (!info) return; - const { indexSignature, container, otherMembers, parameterName, parameterType } = info; - - doChange(changes, context.sourceFile, indexSignature, container, otherMembers, parameterName, parameterType); + const info = getInfo(diag.file, diag.start); + if (info) doChange(changes, diag.file, info); }) }); - function isFixableParameterName(node: Node): boolean { - return node && node.parent && node.parent.parent && node.parent.parent.parent && !isClassDeclaration(node.parent.parent.parent); - } - - function getFixableSignatureAtPosition(sourceFile: SourceFile, pos: number): Info | undefined { + interface Info { readonly indexSignature: IndexSignatureDeclaration; readonly container: FixableDeclaration; } + function getInfo(sourceFile: SourceFile, pos: number): Info | undefined { const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); - if (!isFixableParameterName(token)) return undefined; + const indexSignature = cast(token.parent.parent, isIndexSignatureDeclaration); + if (isClassDeclaration(indexSignature.parent)) return undefined; + const container = isInterfaceDeclaration(indexSignature.parent) ? indexSignature.parent : cast(indexSignature.parent.parent, isTypeAliasDeclaration); + return { indexSignature, container }; + } - const indexSignature = token.parent.parent; - const container = isInterfaceDeclaration(indexSignature.parent) ? indexSignature.parent : indexSignature.parent.parent; + function createTypeAliasFromInterface(declaration: FixableDeclaration, type: TypeNode): TypeAliasDeclaration { + return createTypeAliasDeclaration(declaration.decorators, declaration.modifiers, declaration.name, declaration.typeParameters, type); + } + + function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { indexSignature, container }: Info): void { const members = isInterfaceDeclaration(container) ? container.members : (container.type).members; - const otherMembers = filter(members, member => !isIndexSignatureDeclaration(member)); + const otherMembers = members.filter(member => !isIndexSignatureDeclaration(member)); const parameter = first(indexSignature.parameters); - - return { - indexSignature, - container, - otherMembers, - parameterName: parameter.name, - parameterType: parameter.type! - }; - } - - function getInterfaceHeritageClauses(declaration: FixableDeclaration): NodeArray | undefined { - if (!isInterfaceDeclaration(declaration)) return undefined; - - const heritageClause = getHeritageClause(declaration.heritageClauses, SyntaxKind.ExtendsKeyword); - return heritageClause && heritageClause.types; - } - - function createTypeAliasFromInterface(indexSignature: IndexSignatureDeclaration, declaration: FixableDeclaration, otherMembers: ReadonlyArray, parameterName: Identifier, parameterType: TypeNode) { - const heritageClauses = getInterfaceHeritageClauses(declaration); - const mappedTypeParameter = createTypeParameterDeclaration(parameterName, parameterType); + const mappedTypeParameter = createTypeParameterDeclaration(cast(parameter.name, isIdentifier), parameter.type); const mappedIntersectionType = createMappedTypeNode( hasReadonlyModifier(indexSignature) ? createModifier(SyntaxKind.ReadonlyKeyword) : undefined, mappedTypeParameter, indexSignature.questionToken, indexSignature.type); - - return createTypeAliasDeclaration( - declaration.decorators, - declaration.modifiers, - declaration.name, - declaration.typeParameters, - createIntersectionTypeNode( - concatenate( - heritageClauses, - append([mappedIntersectionType], otherMembers.length ? createTypeLiteralNode(otherMembers) : undefined) - ) - ) - ); - } - - function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, indexSignature: IndexSignatureDeclaration, declaration: FixableDeclaration, otherMembers: ReadonlyArray, parameterName: Identifier, parameterType: TypeNode) { - changes.replaceNode(sourceFile, declaration, createTypeAliasFromInterface(indexSignature, declaration, otherMembers, parameterName, parameterType)); + const intersectionType = createIntersectionTypeNode([ + ...getAllSuperTypeNodes(container), + mappedIntersectionType, + ...(otherMembers.length ? [createTypeLiteralNode(otherMembers)] : emptyArray), + ]); + changes.replaceNode(sourceFile, container, createTypeAliasFromInterface(container, intersectionType)); } }