@@ -26,9 +26,6 @@ namespace ts.codefix {
26
26
errorCodes,
27
27
getCodeActions ( context ) {
28
28
const { sourceFile, program, span : { start } , errorCode, cancellationToken, host } = context ;
29
- if ( isSourceFileJS ( sourceFile ) ) {
30
- return undefined ; // TODO: GH#20113
31
- }
32
29
33
30
const token = getTokenAtPosition ( sourceFile , start ) ;
34
31
let declaration ! : Declaration | undefined ;
@@ -50,7 +47,7 @@ namespace ts.codefix {
50
47
function getDiagnostic ( errorCode : number , token : Node ) : DiagnosticMessage {
51
48
switch ( errorCode ) {
52
49
case Diagnostics . Parameter_0_implicitly_has_an_1_type . code :
53
- return isSetAccessor ( getContainingFunction ( token ) ! ) ? Diagnostics . Infer_type_of_0_from_usage : Diagnostics . Infer_parameter_types_from_usage ; // TODO: GH#18217
50
+ return isSetAccessorDeclaration ( getContainingFunction ( token ) ! ) ? Diagnostics . Infer_type_of_0_from_usage : Diagnostics . Infer_parameter_types_from_usage ; // TODO: GH#18217
54
51
case Diagnostics . Rest_parameter_0_implicitly_has_an_any_type . code :
55
52
return Diagnostics . Infer_parameter_types_from_usage ;
56
53
default :
@@ -59,7 +56,7 @@ namespace ts.codefix {
59
56
}
60
57
61
58
function doChange ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , token : Node , errorCode : number , program : Program , cancellationToken : CancellationToken , markSeen : NodeSeenTracker , host : LanguageServiceHost ) : Declaration | undefined {
62
- if ( ! isParameterPropertyModifier ( token . kind ) && token . kind !== SyntaxKind . Identifier && token . kind !== SyntaxKind . DotDotDotToken ) {
59
+ if ( ! isParameterPropertyModifier ( token . kind ) && token . kind !== SyntaxKind . Identifier && token . kind !== SyntaxKind . DotDotDotToken && token . kind !== SyntaxKind . ThisKeyword ) {
63
60
return undefined ;
64
61
}
65
62
@@ -72,6 +69,14 @@ namespace ts.codefix {
72
69
annotateVariableDeclaration ( changes , sourceFile , parent , program , host , cancellationToken ) ;
73
70
return parent ;
74
71
}
72
+ if ( isPropertyAccessExpression ( parent ) ) {
73
+ const type = inferTypeForVariableFromUsage ( parent . name , program , cancellationToken ) ;
74
+ const typeNode = type && getTypeNodeIfAccessible ( type , parent , program , host ) ;
75
+ if ( typeNode ) {
76
+ changes . tryInsertJSDocType ( sourceFile , parent , typeNode ) ;
77
+ }
78
+ return parent ;
79
+ }
75
80
return undefined ;
76
81
77
82
case Diagnostics . Variable_0_implicitly_has_an_1_type . code : {
@@ -92,7 +97,7 @@ namespace ts.codefix {
92
97
switch ( errorCode ) {
93
98
// Parameter declarations
94
99
case Diagnostics . Parameter_0_implicitly_has_an_1_type . code :
95
- if ( isSetAccessor ( containingFunction ) ) {
100
+ if ( isSetAccessorDeclaration ( containingFunction ) ) {
96
101
annotateSetAccessor ( changes , sourceFile , containingFunction , program , host , cancellationToken ) ;
97
102
return containingFunction ;
98
103
}
@@ -108,15 +113,15 @@ namespace ts.codefix {
108
113
// Get Accessor declarations
109
114
case Diagnostics . Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation . code :
110
115
case Diagnostics . _0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type . code :
111
- if ( isGetAccessor ( containingFunction ) && isIdentifier ( containingFunction . name ) ) {
116
+ if ( isGetAccessorDeclaration ( containingFunction ) && isIdentifier ( containingFunction . name ) ) {
112
117
annotate ( changes , sourceFile , containingFunction , inferTypeForVariableFromUsage ( containingFunction . name , program , cancellationToken ) , program , host ) ;
113
118
return containingFunction ;
114
119
}
115
120
return undefined ;
116
121
117
122
// Set Accessor declarations
118
123
case Diagnostics . Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation . code :
119
- if ( isSetAccessor ( containingFunction ) ) {
124
+ if ( isSetAccessorDeclaration ( containingFunction ) ) {
120
125
annotateSetAccessor ( changes , sourceFile , containingFunction , program , host , cancellationToken ) ;
121
126
return containingFunction ;
122
127
}
@@ -150,35 +155,61 @@ namespace ts.codefix {
150
155
return ;
151
156
}
152
157
153
- const types = inferTypeForParametersFromUsage ( containingFunction , sourceFile , program , cancellationToken ) ||
154
- containingFunction . parameters . map ( p => isIdentifier ( p . name ) ? inferTypeForVariableFromUsage ( p . name , program , cancellationToken ) : undefined ) ;
155
- // We didn't actually find a set of type inference positions matching each parameter position
156
- if ( ! types || containingFunction . parameters . length !== types . length ) {
157
- return ;
158
- }
158
+ const parameterInferences = inferTypeForParametersFromUsage ( containingFunction , sourceFile , program , cancellationToken ) ||
159
+ containingFunction . parameters . map < ParameterInference > ( p => ( {
160
+ declaration : p ,
161
+ type : isIdentifier ( p . name ) ? inferTypeForVariableFromUsage ( p . name , program , cancellationToken ) : undefined
162
+ } ) ) ;
163
+ Debug . assert ( containingFunction . parameters . length === parameterInferences . length ) ;
159
164
160
- zipWith ( containingFunction . parameters , types , ( parameter , type ) => {
161
- if ( ! parameter . type && ! parameter . initializer ) {
162
- annotate ( changes , sourceFile , parameter , type , program , host ) ;
165
+ if ( isInJSFile ( containingFunction ) ) {
166
+ annotateJSDocParameters ( changes , sourceFile , parameterInferences , program , host ) ;
167
+ }
168
+ else {
169
+ for ( const { declaration, type } of parameterInferences ) {
170
+ if ( declaration && ! declaration . type && ! declaration . initializer ) {
171
+ annotate ( changes , sourceFile , declaration , type , program , host ) ;
172
+ }
163
173
}
164
- } ) ;
174
+ }
165
175
}
166
176
167
177
function annotateSetAccessor ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , setAccessorDeclaration : SetAccessorDeclaration , program : Program , host : LanguageServiceHost , cancellationToken : CancellationToken ) : void {
168
178
const param = firstOrUndefined ( setAccessorDeclaration . parameters ) ;
169
179
if ( param && isIdentifier ( setAccessorDeclaration . name ) && isIdentifier ( param . name ) ) {
170
180
const type = inferTypeForVariableFromUsage ( setAccessorDeclaration . name , program , cancellationToken ) ||
171
181
inferTypeForVariableFromUsage ( param . name , program , cancellationToken ) ;
172
- annotate ( changes , sourceFile , param , type , program , host ) ;
182
+ if ( isInJSFile ( setAccessorDeclaration ) ) {
183
+ annotateJSDocParameters ( changes , sourceFile , [ { declaration : param , type } ] , program , host ) ;
184
+ }
185
+ else {
186
+ annotate ( changes , sourceFile , param , type , program , host ) ;
187
+ }
173
188
}
174
189
}
175
190
176
191
function annotate ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , declaration : textChanges . TypeAnnotatable , type : Type | undefined , program : Program , host : LanguageServiceHost ) : void {
177
192
const typeNode = type && getTypeNodeIfAccessible ( type , declaration , program , host ) ;
178
- if ( typeNode ) changes . tryInsertTypeAnnotation ( sourceFile , declaration , typeNode ) ;
193
+ if ( typeNode ) {
194
+ if ( isInJSFile ( sourceFile ) && declaration . kind !== SyntaxKind . PropertySignature ) {
195
+ changes . tryInsertJSDocType ( sourceFile , declaration , typeNode ) ;
196
+ }
197
+ else {
198
+ changes . tryInsertTypeAnnotation ( sourceFile , declaration , typeNode ) ;
199
+ }
200
+ }
179
201
}
180
202
181
- function getTypeNodeIfAccessible ( type : Type , enclosingScope : Node , program : Program , host : LanguageServiceHost ) : TypeNode | undefined {
203
+ function annotateJSDocParameters ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , parameterInferences : ParameterInference [ ] , program : Program , host : LanguageServiceHost ) : void {
204
+ const result = mapDefined ( parameterInferences , inference => {
205
+ const param = inference . declaration ;
206
+ const typeNode = inference . type && getTypeNodeIfAccessible ( inference . type , param , program , host ) ;
207
+ return typeNode && ! param . initializer && ! getJSDocType ( param ) ? { ...inference , typeNode } : undefined ;
208
+ } ) ;
209
+ changes . tryInsertJSDocParameters ( sourceFile , result ) ;
210
+ }
211
+
212
+ function getTypeNodeIfAccessible ( type : Type , enclosingScope : Node , program : Program , host : LanguageServiceHost ) : TypeNode | undefined {
182
213
const checker = program . getTypeChecker ( ) ;
183
214
let typeIsAccessible = true ;
184
215
const notAccessible = ( ) => { typeIsAccessible = false ; } ;
@@ -212,7 +243,7 @@ namespace ts.codefix {
212
243
return InferFromReference . inferTypeFromReferences ( getReferences ( token , program , cancellationToken ) , program . getTypeChecker ( ) , cancellationToken ) ;
213
244
}
214
245
215
- function inferTypeForParametersFromUsage ( containingFunction : FunctionLikeDeclaration , sourceFile : SourceFile , program : Program , cancellationToken : CancellationToken ) : ( Type | undefined ) [ ] | undefined {
246
+ function inferTypeForParametersFromUsage ( containingFunction : FunctionLikeDeclaration , sourceFile : SourceFile , program : Program , cancellationToken : CancellationToken ) : ParameterInference [ ] | undefined {
216
247
switch ( containingFunction . kind ) {
217
248
case SyntaxKind . Constructor :
218
249
case SyntaxKind . FunctionExpression :
@@ -228,6 +259,13 @@ namespace ts.codefix {
228
259
}
229
260
}
230
261
262
+ interface ParameterInference {
263
+ declaration : ParameterDeclaration ;
264
+ type ?: Type ;
265
+ typeNode ?: TypeNode ;
266
+ isOptional ?: boolean ;
267
+ }
268
+
231
269
namespace InferFromReference {
232
270
interface CallContext {
233
271
argumentTypes : Type [ ] ;
@@ -255,7 +293,7 @@ namespace ts.codefix {
255
293
return getTypeFromUsageContext ( usageContext , checker ) ;
256
294
}
257
295
258
- export function inferTypeForParametersFromReferences ( references : ReadonlyArray < Identifier > , declaration : FunctionLikeDeclaration , checker : TypeChecker , cancellationToken : CancellationToken ) : ( Type | undefined ) [ ] | undefined {
296
+ export function inferTypeForParametersFromReferences ( references : ReadonlyArray < Identifier > , declaration : FunctionLikeDeclaration , checker : TypeChecker , cancellationToken : CancellationToken ) : ParameterInference [ ] | undefined {
259
297
if ( references . length === 0 ) {
260
298
return undefined ;
261
299
}
@@ -274,8 +312,10 @@ namespace ts.codefix {
274
312
return callContexts && declaration . parameters . map ( ( parameter , parameterIndex ) => {
275
313
const types : Type [ ] = [ ] ;
276
314
const isRest = isRestParameter ( parameter ) ;
315
+ let isOptional = false ;
277
316
for ( const callContext of callContexts ) {
278
317
if ( callContext . argumentTypes . length <= parameterIndex ) {
318
+ isOptional = isInJSFile ( declaration ) ;
279
319
continue ;
280
320
}
281
321
@@ -289,10 +329,14 @@ namespace ts.codefix {
289
329
}
290
330
}
291
331
if ( ! types . length ) {
292
- return undefined ;
332
+ return { declaration : parameter } ;
293
333
}
294
334
const type = checker . getWidenedType ( checker . getUnionType ( types , UnionReduction . Subtype ) ) ;
295
- return isRest ? checker . createArrayType ( type ) : type ;
335
+ return {
336
+ type : isRest ? checker . createArrayType ( type ) : type ,
337
+ isOptional : isOptional && ! isRest ,
338
+ declaration : parameter
339
+ } ;
296
340
} ) ;
297
341
}
298
342
0 commit comments