@@ -90,8 +90,8 @@ namespace ts.refactor.convertParamsToDestructuredObject {
9090 function groupReferences ( referenceEntries : ReadonlyArray < FindAllReferences . Entry > ) : GroupedReferences {
9191 const classReferences : ClassReferences = { accessExpressions : [ ] , typeUsages : [ ] } ;
9292 const groupedReferences : GroupedReferences = { functionCalls : [ ] , declarations : [ ] , classReferences, valid : true } ;
93- const functionSymbols = map ( functionNames , checker . getSymbolAtLocation ) ;
94- const classSymbols = map ( classNames , checker . getSymbolAtLocation ) ;
93+ const functionSymbols = map ( functionNames , getSymbolTargetAtLocation ) ;
94+ const classSymbols = map ( classNames , getSymbolTargetAtLocation ) ;
9595 const isConstructor = isConstructorDeclaration ( functionDeclaration ) ;
9696
9797 for ( const entry of referenceEntries ) {
@@ -111,7 +111,11 @@ namespace ts.refactor.convertParamsToDestructuredObject {
111111 So we need to add a special case for this because when calling a constructor of a class through one of its subclasses,
112112 the symbols are going to be different.
113113 */
114- if ( contains ( functionSymbols , checker . getSymbolAtLocation ( entry . node ) , symbolComparer ) || isNewExpressionTarget ( entry . node ) ) {
114+ if ( contains ( functionSymbols , getSymbolTargetAtLocation ( entry . node ) ) || isNewExpressionTarget ( entry . node ) ) {
115+ const importOrExportReference = entryToImportOrExport ( entry ) ;
116+ if ( importOrExportReference ) {
117+ continue ;
118+ }
115119 const decl = entryToDeclaration ( entry ) ;
116120 if ( decl ) {
117121 groupedReferences . declarations . push ( decl ) ;
@@ -125,7 +129,12 @@ namespace ts.refactor.convertParamsToDestructuredObject {
125129 }
126130 }
127131 // if the refactored function is a constructor, we must also check if the references to its class are valid
128- if ( isConstructor && contains ( classSymbols , checker . getSymbolAtLocation ( entry . node ) , symbolComparer ) ) {
132+ if ( isConstructor && contains ( classSymbols , getSymbolTargetAtLocation ( entry . node ) ) ) {
133+ const importOrExportReference = entryToImportOrExport ( entry ) ;
134+ if ( importOrExportReference ) {
135+ continue ;
136+ }
137+
129138 const decl = entryToDeclaration ( entry ) ;
130139 if ( decl ) {
131140 groupedReferences . declarations . push ( decl ) ;
@@ -153,10 +162,27 @@ namespace ts.refactor.convertParamsToDestructuredObject {
153162
154163 return groupedReferences ;
155164 }
165+
166+ function getSymbolTargetAtLocation ( node : Node ) {
167+ const symbol = checker . getSymbolAtLocation ( node ) ;
168+ return symbol && getSymbolTarget ( symbol , checker ) ;
169+ }
156170 }
157171
158- function symbolComparer ( a : Symbol , b : Symbol ) : boolean {
159- return getSymbolTarget ( a ) === getSymbolTarget ( b ) ;
172+ function entryToImportOrExport ( entry : FindAllReferences . NodeEntry ) : Node | undefined {
173+ const node = entry . node ;
174+
175+ if ( isImportSpecifier ( node . parent )
176+ || isImportClause ( node . parent )
177+ || isImportEqualsDeclaration ( node . parent )
178+ || isNamespaceImport ( node . parent ) ) {
179+ return node ;
180+ }
181+
182+ if ( isExportSpecifier ( node . parent ) || isExportAssignment ( node . parent ) ) {
183+ return node ;
184+ }
185+ return undefined ;
160186 }
161187
162188 function entryToDeclaration ( entry : FindAllReferences . NodeEntry ) : Node | undefined {
@@ -171,37 +197,31 @@ namespace ts.refactor.convertParamsToDestructuredObject {
171197 const functionReference = entry . node ;
172198 const parent = functionReference . parent ;
173199 switch ( parent . kind ) {
174- // Function call ( foo(...) or super(...))
200+ // foo(...) or super(...) or new Foo(... )
175201 case SyntaxKind . CallExpression :
176- const callExpression = tryCast ( parent , isCallExpression ) ;
177- if ( callExpression && callExpression . expression === functionReference ) {
178- return callExpression ;
179- }
180- break ;
181- // Constructor call (new Foo(...))
182202 case SyntaxKind . NewExpression :
183- const newExpression = tryCast ( parent , isNewExpression ) ;
184- if ( newExpression && newExpression . expression === functionReference ) {
185- return newExpression ;
203+ const callOrNewExpression = tryCast ( parent , isCallOrNewExpression ) ;
204+ if ( callOrNewExpression && callOrNewExpression . expression === functionReference ) {
205+ return callOrNewExpression ;
186206 }
187207 break ;
188- // Method call ( x.foo(...) )
208+ // x.foo(...)
189209 case SyntaxKind . PropertyAccessExpression :
190210 const propertyAccessExpression = tryCast ( parent , isPropertyAccessExpression ) ;
191211 if ( propertyAccessExpression && propertyAccessExpression . parent && propertyAccessExpression . name === functionReference ) {
192- const callExpression = tryCast ( propertyAccessExpression . parent , isCallExpression ) ;
193- if ( callExpression && callExpression . expression === propertyAccessExpression ) {
194- return callExpression ;
212+ const callOrNewExpression = tryCast ( propertyAccessExpression . parent , isCallOrNewExpression ) ;
213+ if ( callOrNewExpression && callOrNewExpression . expression === propertyAccessExpression ) {
214+ return callOrNewExpression ;
195215 }
196216 }
197217 break ;
198- // Method call ( x["foo"](...) )
218+ // x["foo"](...)
199219 case SyntaxKind . ElementAccessExpression :
200220 const elementAccessExpression = tryCast ( parent , isElementAccessExpression ) ;
201221 if ( elementAccessExpression && elementAccessExpression . parent && elementAccessExpression . argumentExpression === functionReference ) {
202- const callExpression = tryCast ( elementAccessExpression . parent , isCallExpression ) ;
203- if ( callExpression && callExpression . expression === elementAccessExpression ) {
204- return callExpression ;
222+ const callOrNewExpression = tryCast ( elementAccessExpression . parent , isCallOrNewExpression ) ;
223+ if ( callOrNewExpression && callOrNewExpression . expression === elementAccessExpression ) {
224+ return callOrNewExpression ;
205225 }
206226 }
207227 break ;
@@ -244,7 +264,7 @@ namespace ts.refactor.convertParamsToDestructuredObject {
244264
245265 function getFunctionDeclarationAtPosition ( file : SourceFile , startPosition : number , checker : TypeChecker ) : ValidFunctionDeclaration | undefined {
246266 const node = getTouchingToken ( file , startPosition ) ;
247- const functionDeclaration = getContainingFunction ( node ) ;
267+ const functionDeclaration = getContainingFunctionDeclaration ( node ) ;
248268
249269 // don't offer refactor on top-level JSDoc
250270 if ( isTopLevelJSDoc ( node ) ) return undefined ;
@@ -267,25 +287,21 @@ namespace ts.refactor.convertParamsToDestructuredObject {
267287 }
268288
269289 function isValidFunctionDeclaration (
270- functionDeclaration : SignatureDeclaration ,
290+ functionDeclaration : FunctionLikeDeclaration ,
271291 checker : TypeChecker ) : functionDeclaration is ValidFunctionDeclaration {
272292 if ( ! isValidParameterNodeArray ( functionDeclaration . parameters , checker ) ) return false ;
273293 switch ( functionDeclaration . kind ) {
274294 case SyntaxKind . FunctionDeclaration :
295+ return hasNameOrDefault ( functionDeclaration ) && isSingleImplementation ( functionDeclaration , checker ) ;
275296 case SyntaxKind . MethodDeclaration :
276- return ! ! functionDeclaration . name
277- && ! ! functionDeclaration . body
278- && ! checker . isImplementationOfOverload ( functionDeclaration ) ;
297+ return isSingleImplementation ( functionDeclaration , checker ) ;
279298 case SyntaxKind . Constructor :
280299 if ( isClassDeclaration ( functionDeclaration . parent ) ) {
281- return ! ! functionDeclaration . body
282- && ! ! functionDeclaration . parent . name
283- && ! checker . isImplementationOfOverload ( functionDeclaration ) ;
300+ return hasNameOrDefault ( functionDeclaration . parent ) && isSingleImplementation ( functionDeclaration , checker ) ;
284301 }
285302 else {
286303 return isValidVariableDeclaration ( functionDeclaration . parent . parent )
287- && ! ! functionDeclaration . body
288- && ! checker . isImplementationOfOverload ( functionDeclaration ) ;
304+ && isSingleImplementation ( functionDeclaration , checker ) ;
289305 }
290306 case SyntaxKind . FunctionExpression :
291307 case SyntaxKind . ArrowFunction :
@@ -294,6 +310,18 @@ namespace ts.refactor.convertParamsToDestructuredObject {
294310 return false ;
295311 }
296312
313+ function isSingleImplementation ( functionDeclaration : FunctionLikeDeclaration , checker : TypeChecker ) : boolean {
314+ return ! ! functionDeclaration . body && ! checker . isImplementationOfOverload ( functionDeclaration ) ;
315+ }
316+
317+ function hasNameOrDefault ( functionOrClassDeclaration : FunctionDeclaration | ClassDeclaration ) : boolean {
318+ if ( ! functionOrClassDeclaration . name ) {
319+ const defaultKeyword = findModifier ( functionOrClassDeclaration , SyntaxKind . DefaultKeyword ) ;
320+ return ! ! defaultKeyword ;
321+ }
322+ return true ;
323+ }
324+
297325 function isValidParameterNodeArray (
298326 parameters : NodeArray < ParameterDeclaration > ,
299327 checker : TypeChecker ) : parameters is ValidParameterNodeArray {
@@ -488,11 +516,17 @@ namespace ts.refactor.convertParamsToDestructuredObject {
488516 return getTextOfIdentifierOrLiteral ( paramDeclaration . name ) ;
489517 }
490518
491- function getClassNames ( constructorDeclaration : ValidConstructor ) : Identifier [ ] {
519+ function getClassNames ( constructorDeclaration : ValidConstructor ) : ( Identifier | Modifier ) [ ] {
492520 switch ( constructorDeclaration . parent . kind ) {
493521 case SyntaxKind . ClassDeclaration :
494522 const classDeclaration = constructorDeclaration . parent ;
495- return [ classDeclaration . name ] ;
523+ if ( classDeclaration . name ) return [ classDeclaration . name ] ;
524+ // If the class declaration doesn't have a name, it should have a default modifier.
525+ // We validated this in `isValidFunctionDeclaration` through `hasNameOrDefault`
526+ const defaultModifier = Debug . assertDefined (
527+ findModifier ( classDeclaration , SyntaxKind . DefaultKeyword ) ,
528+ "Nameless class declaration should be a default export" ) ;
529+ return [ defaultModifier ] ;
496530 case SyntaxKind . ClassExpression :
497531 const classExpression = constructorDeclaration . parent ;
498532 const variableDeclaration = constructorDeclaration . parent . parent ;
@@ -505,10 +539,19 @@ namespace ts.refactor.convertParamsToDestructuredObject {
505539 function getFunctionNames ( functionDeclaration : ValidFunctionDeclaration ) : Node [ ] {
506540 switch ( functionDeclaration . kind ) {
507541 case SyntaxKind . FunctionDeclaration :
542+ if ( functionDeclaration . name ) return [ functionDeclaration . name ] ;
543+ // If the function declaration doesn't have a name, it should have a default modifier.
544+ // We validated this in `isValidFunctionDeclaration` through `hasNameOrDefault`
545+ const defaultModifier = Debug . assertDefined (
546+ findModifier ( functionDeclaration , SyntaxKind . DefaultKeyword ) ,
547+ "Nameless function declaration should be a default export" ) ;
548+ return [ defaultModifier ] ;
508549 case SyntaxKind . MethodDeclaration :
509550 return [ functionDeclaration . name ] ;
510551 case SyntaxKind . Constructor :
511- const ctrKeyword = findChildOfKind ( functionDeclaration , SyntaxKind . ConstructorKeyword , functionDeclaration . getSourceFile ( ) ) ! ;
552+ const ctrKeyword = Debug . assertDefined (
553+ findChildOfKind ( functionDeclaration , SyntaxKind . ConstructorKeyword , functionDeclaration . getSourceFile ( ) ) ,
554+ "Constructor declaration should have constructor keyword" ) ;
512555 if ( functionDeclaration . parent . kind === SyntaxKind . ClassExpression ) {
513556 const variableDeclaration = functionDeclaration . parent . parent ;
514557 return [ variableDeclaration . name , ctrKeyword ] ;
@@ -532,13 +575,12 @@ namespace ts.refactor.convertParamsToDestructuredObject {
532575 }
533576
534577 interface ValidConstructor extends ConstructorDeclaration {
535- parent : ( ClassDeclaration & { name : Identifier } ) | ( ClassExpression & { parent : ValidVariableDeclaration } ) ;
578+ parent : ClassDeclaration | ( ClassExpression & { parent : ValidVariableDeclaration } ) ;
536579 parameters : NodeArray < ValidParameterDeclaration > ;
537580 body : FunctionBody ;
538581 }
539582
540583 interface ValidFunction extends FunctionDeclaration {
541- name : Identifier ;
542584 parameters : NodeArray < ValidParameterDeclaration > ;
543585 body : FunctionBody ;
544586 }
0 commit comments