1/* @internal */ 2namespace ts.codefix { 3 const fixId = "fixConvertToMappedObjectType"; 4 const errorCodes = [Diagnostics.An_index_signature_parameter_type_cannot_be_a_literal_type_or_generic_type_Consider_using_a_mapped_object_type_instead.code]; 5 6 type FixableDeclaration = InterfaceDeclaration | TypeAliasDeclaration; 7 8 registerCodeFix({ 9 errorCodes, 10 getCodeActions: function getCodeActionsToConvertToMappedTypeObject(context) { 11 const { sourceFile, span } = context; 12 const info = getInfo(sourceFile, span.start); 13 if (!info) return undefined; 14 const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info)); 15 const name = idText(info.container.name); 16 return [createCodeFixAction(fixId, changes, [Diagnostics.Convert_0_to_mapped_object_type, name], fixId, [Diagnostics.Convert_0_to_mapped_object_type, name])]; 17 }, 18 fixIds: [fixId], 19 getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { 20 const info = getInfo(diag.file, diag.start); 21 if (info) doChange(changes, diag.file, info); 22 }) 23 }); 24 25 interface Info { readonly indexSignature: IndexSignatureDeclaration; readonly container: FixableDeclaration; } 26 function getInfo(sourceFile: SourceFile, pos: number): Info | undefined { 27 const token = getTokenAtPosition(sourceFile, pos); 28 const indexSignature = tryCast(token.parent.parent, isIndexSignatureDeclaration); 29 if (!indexSignature) return undefined; 30 31 const container = isInterfaceDeclaration(indexSignature.parent) ? indexSignature.parent : tryCast(indexSignature.parent.parent, isTypeAliasDeclaration); 32 if (!container) return undefined; 33 34 return { indexSignature, container }; 35 } 36 37 function createTypeAliasFromInterface(declaration: FixableDeclaration, type: TypeNode): TypeAliasDeclaration { 38 return factory.createTypeAliasDeclaration(declaration.modifiers, declaration.name, declaration.typeParameters, type); 39 } 40 41 function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { indexSignature, container }: Info): void { 42 const members = isInterfaceDeclaration(container) ? container.members : (container.type as TypeLiteralNode).members; 43 const otherMembers = members.filter(member => !isIndexSignatureDeclaration(member)); 44 const parameter = first(indexSignature.parameters); 45 const mappedTypeParameter = factory.createTypeParameterDeclaration(/*modifiers*/ undefined, cast(parameter.name, isIdentifier), parameter.type); 46 const mappedIntersectionType = factory.createMappedTypeNode( 47 hasEffectiveReadonlyModifier(indexSignature) ? factory.createModifier(SyntaxKind.ReadonlyKeyword) : undefined, 48 mappedTypeParameter, 49 /*nameType*/ undefined, 50 indexSignature.questionToken, 51 indexSignature.type, 52 /*members*/ undefined); 53 const intersectionType = factory.createIntersectionTypeNode([ 54 ...getAllSuperTypeNodes(container), 55 mappedIntersectionType, 56 ...(otherMembers.length ? [factory.createTypeLiteralNode(otherMembers)] : emptyArray), 57 ]); 58 changes.replaceNode(sourceFile, container, createTypeAliasFromInterface(container, intersectionType)); 59 } 60} 61