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