@@ -90,8 +90,8 @@ namespace ts.refactor.convertParamsToDestructuredObject {
90
90
function groupReferences ( referenceEntries : ReadonlyArray < FindAllReferences . Entry > ) : GroupedReferences {
91
91
const classReferences : ClassReferences = { accessExpressions : [ ] , typeUsages : [ ] } ;
92
92
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 ) ;
95
95
const isConstructor = isConstructorDeclaration ( functionDeclaration ) ;
96
96
97
97
for ( const entry of referenceEntries ) {
@@ -111,7 +111,11 @@ namespace ts.refactor.convertParamsToDestructuredObject {
111
111
So we need to add a special case for this because when calling a constructor of a class through one of its subclasses,
112
112
the symbols are going to be different.
113
113
*/
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
+ }
115
119
const decl = entryToDeclaration ( entry ) ;
116
120
if ( decl ) {
117
121
groupedReferences . declarations . push ( decl ) ;
@@ -125,7 +129,12 @@ namespace ts.refactor.convertParamsToDestructuredObject {
125
129
}
126
130
}
127
131
// 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
+
129
138
const decl = entryToDeclaration ( entry ) ;
130
139
if ( decl ) {
131
140
groupedReferences . declarations . push ( decl ) ;
@@ -153,10 +162,27 @@ namespace ts.refactor.convertParamsToDestructuredObject {
153
162
154
163
return groupedReferences ;
155
164
}
165
+
166
+ function getSymbolTargetAtLocation ( node : Node ) {
167
+ const symbol = checker . getSymbolAtLocation ( node ) ;
168
+ return symbol && getSymbolTarget ( symbol , checker ) ;
169
+ }
156
170
}
157
171
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 ;
160
186
}
161
187
162
188
function entryToDeclaration ( entry : FindAllReferences . NodeEntry ) : Node | undefined {
@@ -171,37 +197,31 @@ namespace ts.refactor.convertParamsToDestructuredObject {
171
197
const functionReference = entry . node ;
172
198
const parent = functionReference . parent ;
173
199
switch ( parent . kind ) {
174
- // Function call ( foo(...) or super(...))
200
+ // foo(...) or super(...) or new Foo(... )
175
201
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(...))
182
202
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 ;
186
206
}
187
207
break ;
188
- // Method call ( x.foo(...) )
208
+ // x.foo(...)
189
209
case SyntaxKind . PropertyAccessExpression :
190
210
const propertyAccessExpression = tryCast ( parent , isPropertyAccessExpression ) ;
191
211
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 ;
195
215
}
196
216
}
197
217
break ;
198
- // Method call ( x["foo"](...) )
218
+ // x["foo"](...)
199
219
case SyntaxKind . ElementAccessExpression :
200
220
const elementAccessExpression = tryCast ( parent , isElementAccessExpression ) ;
201
221
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 ;
205
225
}
206
226
}
207
227
break ;
@@ -244,7 +264,7 @@ namespace ts.refactor.convertParamsToDestructuredObject {
244
264
245
265
function getFunctionDeclarationAtPosition ( file : SourceFile , startPosition : number , checker : TypeChecker ) : ValidFunctionDeclaration | undefined {
246
266
const node = getTouchingToken ( file , startPosition ) ;
247
- const functionDeclaration = getContainingFunction ( node ) ;
267
+ const functionDeclaration = getContainingFunctionDeclaration ( node ) ;
248
268
249
269
// don't offer refactor on top-level JSDoc
250
270
if ( isTopLevelJSDoc ( node ) ) return undefined ;
@@ -267,25 +287,21 @@ namespace ts.refactor.convertParamsToDestructuredObject {
267
287
}
268
288
269
289
function isValidFunctionDeclaration (
270
- functionDeclaration : SignatureDeclaration ,
290
+ functionDeclaration : FunctionLikeDeclaration ,
271
291
checker : TypeChecker ) : functionDeclaration is ValidFunctionDeclaration {
272
292
if ( ! isValidParameterNodeArray ( functionDeclaration . parameters , checker ) ) return false ;
273
293
switch ( functionDeclaration . kind ) {
274
294
case SyntaxKind . FunctionDeclaration :
295
+ return hasNameOrDefault ( functionDeclaration ) && isSingleImplementation ( functionDeclaration , checker ) ;
275
296
case SyntaxKind . MethodDeclaration :
276
- return ! ! functionDeclaration . name
277
- && ! ! functionDeclaration . body
278
- && ! checker . isImplementationOfOverload ( functionDeclaration ) ;
297
+ return isSingleImplementation ( functionDeclaration , checker ) ;
279
298
case SyntaxKind . Constructor :
280
299
if ( isClassDeclaration ( functionDeclaration . parent ) ) {
281
- return ! ! functionDeclaration . body
282
- && ! ! functionDeclaration . parent . name
283
- && ! checker . isImplementationOfOverload ( functionDeclaration ) ;
300
+ return hasNameOrDefault ( functionDeclaration . parent ) && isSingleImplementation ( functionDeclaration , checker ) ;
284
301
}
285
302
else {
286
303
return isValidVariableDeclaration ( functionDeclaration . parent . parent )
287
- && ! ! functionDeclaration . body
288
- && ! checker . isImplementationOfOverload ( functionDeclaration ) ;
304
+ && isSingleImplementation ( functionDeclaration , checker ) ;
289
305
}
290
306
case SyntaxKind . FunctionExpression :
291
307
case SyntaxKind . ArrowFunction :
@@ -294,6 +310,18 @@ namespace ts.refactor.convertParamsToDestructuredObject {
294
310
return false ;
295
311
}
296
312
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
+
297
325
function isValidParameterNodeArray (
298
326
parameters : NodeArray < ParameterDeclaration > ,
299
327
checker : TypeChecker ) : parameters is ValidParameterNodeArray {
@@ -488,11 +516,17 @@ namespace ts.refactor.convertParamsToDestructuredObject {
488
516
return getTextOfIdentifierOrLiteral ( paramDeclaration . name ) ;
489
517
}
490
518
491
- function getClassNames ( constructorDeclaration : ValidConstructor ) : Identifier [ ] {
519
+ function getClassNames ( constructorDeclaration : ValidConstructor ) : ( Identifier | Modifier ) [ ] {
492
520
switch ( constructorDeclaration . parent . kind ) {
493
521
case SyntaxKind . ClassDeclaration :
494
522
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 ] ;
496
530
case SyntaxKind . ClassExpression :
497
531
const classExpression = constructorDeclaration . parent ;
498
532
const variableDeclaration = constructorDeclaration . parent . parent ;
@@ -505,10 +539,19 @@ namespace ts.refactor.convertParamsToDestructuredObject {
505
539
function getFunctionNames ( functionDeclaration : ValidFunctionDeclaration ) : Node [ ] {
506
540
switch ( functionDeclaration . kind ) {
507
541
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 ] ;
508
549
case SyntaxKind . MethodDeclaration :
509
550
return [ functionDeclaration . name ] ;
510
551
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" ) ;
512
555
if ( functionDeclaration . parent . kind === SyntaxKind . ClassExpression ) {
513
556
const variableDeclaration = functionDeclaration . parent . parent ;
514
557
return [ variableDeclaration . name , ctrKeyword ] ;
@@ -532,13 +575,12 @@ namespace ts.refactor.convertParamsToDestructuredObject {
532
575
}
533
576
534
577
interface ValidConstructor extends ConstructorDeclaration {
535
- parent : ( ClassDeclaration & { name : Identifier } ) | ( ClassExpression & { parent : ValidVariableDeclaration } ) ;
578
+ parent : ClassDeclaration | ( ClassExpression & { parent : ValidVariableDeclaration } ) ;
536
579
parameters : NodeArray < ValidParameterDeclaration > ;
537
580
body : FunctionBody ;
538
581
}
539
582
540
583
interface ValidFunction extends FunctionDeclaration {
541
- name : Identifier ;
542
584
parameters : NodeArray < ValidParameterDeclaration > ;
543
585
body : FunctionBody ;
544
586
}
0 commit comments