1/* @internal */ 2namespace ts.refactor.addOrRemoveBracesToArrowFunction { 3 const refactorName = "Convert overload list to single signature"; 4 const refactorDescription = Diagnostics.Convert_overload_list_to_single_signature.message; 5 6 const functionOverloadAction = { 7 name: refactorName, 8 description: refactorDescription, 9 kind: "refactor.rewrite.function.overloadList", 10 }; 11 registerRefactor(refactorName, { 12 kinds: [functionOverloadAction.kind], 13 getEditsForAction, 14 getAvailableActions 15 }); 16 17 function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { 18 const { file, startPosition, program } = context; 19 const info = getConvertableOverloadListAtPosition(file, startPosition, program); 20 if (!info) return emptyArray; 21 22 return [{ 23 name: refactorName, 24 description: refactorDescription, 25 actions: [functionOverloadAction] 26 }]; 27 } 28 29 function getEditsForAction(context: RefactorContext): RefactorEditInfo | undefined { 30 const { file, startPosition, program } = context; 31 const signatureDecls = getConvertableOverloadListAtPosition(file, startPosition, program); 32 if (!signatureDecls) return undefined; 33 34 const checker = program.getTypeChecker(); 35 36 const lastDeclaration = signatureDecls[signatureDecls.length - 1]; 37 let updated = lastDeclaration; 38 switch (lastDeclaration.kind) { 39 case SyntaxKind.MethodSignature: { 40 updated = factory.updateMethodSignature( 41 lastDeclaration, 42 lastDeclaration.modifiers, 43 lastDeclaration.name, 44 lastDeclaration.questionToken, 45 lastDeclaration.typeParameters, 46 getNewParametersForCombinedSignature(signatureDecls), 47 lastDeclaration.type, 48 ); 49 break; 50 } 51 case SyntaxKind.MethodDeclaration: { 52 updated = factory.updateMethodDeclaration( 53 lastDeclaration, 54 lastDeclaration.decorators, 55 lastDeclaration.modifiers, 56 lastDeclaration.asteriskToken, 57 lastDeclaration.name, 58 lastDeclaration.questionToken, 59 lastDeclaration.typeParameters, 60 getNewParametersForCombinedSignature(signatureDecls), 61 lastDeclaration.type, 62 lastDeclaration.body 63 ); 64 break; 65 } 66 case SyntaxKind.CallSignature: { 67 updated = factory.updateCallSignature( 68 lastDeclaration, 69 lastDeclaration.typeParameters, 70 getNewParametersForCombinedSignature(signatureDecls), 71 lastDeclaration.type, 72 ); 73 break; 74 } 75 case SyntaxKind.Constructor: { 76 updated = factory.updateConstructorDeclaration( 77 lastDeclaration, 78 lastDeclaration.decorators, 79 lastDeclaration.modifiers, 80 getNewParametersForCombinedSignature(signatureDecls), 81 lastDeclaration.body 82 ); 83 break; 84 } 85 case SyntaxKind.ConstructSignature: { 86 updated = factory.updateConstructSignature( 87 lastDeclaration, 88 lastDeclaration.typeParameters, 89 getNewParametersForCombinedSignature(signatureDecls), 90 lastDeclaration.type, 91 ); 92 break; 93 } 94 case SyntaxKind.FunctionDeclaration: { 95 updated = factory.updateFunctionDeclaration( 96 lastDeclaration, 97 lastDeclaration.decorators, 98 lastDeclaration.modifiers, 99 lastDeclaration.asteriskToken, 100 lastDeclaration.name, 101 lastDeclaration.typeParameters, 102 getNewParametersForCombinedSignature(signatureDecls), 103 lastDeclaration.type, 104 lastDeclaration.body 105 ); 106 break; 107 } 108 default: return Debug.failBadSyntaxKind(lastDeclaration, "Unhandled signature kind in overload list conversion refactoring"); 109 } 110 111 if (updated === lastDeclaration) { 112 return; // No edits to apply, do nothing 113 } 114 115 const edits = textChanges.ChangeTracker.with(context, t => { 116 t.replaceNodeRange(file, signatureDecls[0], signatureDecls[signatureDecls.length - 1], updated); 117 }); 118 119 return { renameFilename: undefined, renameLocation: undefined, edits }; 120 121 function getNewParametersForCombinedSignature(signatureDeclarations: (MethodSignature | MethodDeclaration | CallSignatureDeclaration | ConstructorDeclaration | ConstructSignatureDeclaration | FunctionDeclaration)[]): NodeArray<ParameterDeclaration> { 122 const lastSig = signatureDeclarations[signatureDeclarations.length - 1]; 123 if (isFunctionLikeDeclaration(lastSig) && lastSig.body) { 124 // Trim away implementation signature arguments (they should already be compatible with overloads, but are likely less precise to guarantee compatability with the overloads) 125 signatureDeclarations = signatureDeclarations.slice(0, signatureDeclarations.length - 1); 126 } 127 return factory.createNodeArray([ 128 factory.createParameterDeclaration( 129 /*decorators*/ undefined, 130 /*modifiers*/ undefined, 131 factory.createToken(SyntaxKind.DotDotDotToken), 132 "args", 133 /*questionToken*/ undefined, 134 factory.createUnionTypeNode(map(signatureDeclarations, convertSignatureParametersToTuple)) 135 ) 136 ]); 137 } 138 139 function convertSignatureParametersToTuple(decl: MethodSignature | MethodDeclaration | CallSignatureDeclaration | ConstructorDeclaration | ConstructSignatureDeclaration | FunctionDeclaration): TupleTypeNode { 140 const members = map(decl.parameters, convertParameterToNamedTupleMember); 141 return setEmitFlags(factory.createTupleTypeNode(members), some(members, m => !!length(getSyntheticLeadingComments(m))) ? EmitFlags.None : EmitFlags.SingleLine); 142 } 143 144 function convertParameterToNamedTupleMember(p: ParameterDeclaration): NamedTupleMember { 145 Debug.assert(isIdentifier(p.name)); // This is checked during refactoring applicability checking 146 const result = setTextRange(factory.createNamedTupleMember( 147 p.dotDotDotToken, 148 p.name, 149 p.questionToken, 150 p.type || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword) 151 ), p); 152 const parameterDocComment = p.symbol && p.symbol.getDocumentationComment(checker); 153 if (parameterDocComment) { 154 const newComment = displayPartsToString(parameterDocComment); 155 if (newComment.length) { 156 setSyntheticLeadingComments(result, [{ 157 text: `* 158${newComment.split("\n").map(c => ` * ${c}`).join("\n")} 159 `, 160 kind: SyntaxKind.MultiLineCommentTrivia, 161 pos: -1, 162 end: -1, 163 hasTrailingNewLine: true, 164 hasLeadingNewline: true, 165 }]); 166 } 167 } 168 return result; 169 } 170 171 } 172 173 function isConvertableSignatureDeclaration(d: Node): d is MethodSignature | MethodDeclaration | CallSignatureDeclaration | ConstructorDeclaration | ConstructSignatureDeclaration | FunctionDeclaration { 174 switch (d.kind) { 175 case SyntaxKind.MethodSignature: 176 case SyntaxKind.MethodDeclaration: 177 case SyntaxKind.CallSignature: 178 case SyntaxKind.Constructor: 179 case SyntaxKind.ConstructSignature: 180 case SyntaxKind.FunctionDeclaration: 181 return true; 182 } 183 return false; 184 } 185 186 function getConvertableOverloadListAtPosition(file: SourceFile, startPosition: number, program: Program) { 187 const node = getTokenAtPosition(file, startPosition); 188 const containingDecl = findAncestor(node, isConvertableSignatureDeclaration); 189 if (!containingDecl) { 190 return; 191 } 192 const checker = program.getTypeChecker(); 193 const signatureSymbol = containingDecl.symbol; 194 if (!signatureSymbol) { 195 return; 196 } 197 const decls = signatureSymbol.declarations; 198 if (length(decls) <= 1) { 199 return; 200 } 201 if (!every(decls, d => getSourceFileOfNode(d) === file)) { 202 return; 203 } 204 if (!isConvertableSignatureDeclaration(decls[0])) { 205 return; 206 } 207 const kindOne = decls[0].kind; 208 if (!every(decls, d => d.kind === kindOne)) { 209 return; 210 } 211 const signatureDecls = decls as (MethodSignature | MethodDeclaration | CallSignatureDeclaration | ConstructorDeclaration | ConstructSignatureDeclaration | FunctionDeclaration)[]; 212 if (some(signatureDecls, d => !!d.typeParameters || some(d.parameters, p => !!p.decorators || !!p.modifiers || !isIdentifier(p.name)))) { 213 return; 214 } 215 const signatures = mapDefined(signatureDecls, d => checker.getSignatureFromDeclaration(d)); 216 if (length(signatures) !== length(decls)) { 217 return; 218 } 219 const returnOne = checker.getReturnTypeOfSignature(signatures[0]); 220 if (!every(signatures, s => checker.getReturnTypeOfSignature(s) === returnOne)) { 221 return; 222 } 223 224 return signatureDecls; 225 } 226} 227