@@ -26,9 +26,6 @@ namespace ts.codefix {
2626 errorCodes,
2727 getCodeActions ( context ) {
2828 const { sourceFile, program, span : { start } , errorCode, cancellationToken, host } = context ;
29- if ( isSourceFileJS ( sourceFile ) ) {
30- return undefined ; // TODO: GH#20113
31- }
3229
3330 const token = getTokenAtPosition ( sourceFile , start ) ;
3431 let declaration ! : Declaration | undefined ;
@@ -50,7 +47,7 @@ namespace ts.codefix {
5047 function getDiagnostic ( errorCode : number , token : Node ) : DiagnosticMessage {
5148 switch ( errorCode ) {
5249 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
5451 case Diagnostics . Rest_parameter_0_implicitly_has_an_any_type . code :
5552 return Diagnostics . Infer_parameter_types_from_usage ;
5653 default :
@@ -59,7 +56,7 @@ namespace ts.codefix {
5956 }
6057
6158 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 ) {
6360 return undefined ;
6461 }
6562
@@ -72,6 +69,14 @@ namespace ts.codefix {
7269 annotateVariableDeclaration ( changes , sourceFile , parent , program , host , cancellationToken ) ;
7370 return parent ;
7471 }
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+ }
7580 return undefined ;
7681
7782 case Diagnostics . Variable_0_implicitly_has_an_1_type . code : {
@@ -92,7 +97,7 @@ namespace ts.codefix {
9297 switch ( errorCode ) {
9398 // Parameter declarations
9499 case Diagnostics . Parameter_0_implicitly_has_an_1_type . code :
95- if ( isSetAccessor ( containingFunction ) ) {
100+ if ( isSetAccessorDeclaration ( containingFunction ) ) {
96101 annotateSetAccessor ( changes , sourceFile , containingFunction , program , host , cancellationToken ) ;
97102 return containingFunction ;
98103 }
@@ -108,15 +113,15 @@ namespace ts.codefix {
108113 // Get Accessor declarations
109114 case Diagnostics . Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation . code :
110115 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 ) ) {
112117 annotate ( changes , sourceFile , containingFunction , inferTypeForVariableFromUsage ( containingFunction . name , program , cancellationToken ) , program , host ) ;
113118 return containingFunction ;
114119 }
115120 return undefined ;
116121
117122 // Set Accessor declarations
118123 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 ) ) {
120125 annotateSetAccessor ( changes , sourceFile , containingFunction , program , host , cancellationToken ) ;
121126 return containingFunction ;
122127 }
@@ -150,35 +155,61 @@ namespace ts.codefix {
150155 return ;
151156 }
152157
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 ) ;
159164
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+ }
163173 }
164- } ) ;
174+ }
165175 }
166176
167177 function annotateSetAccessor ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , setAccessorDeclaration : SetAccessorDeclaration , program : Program , host : LanguageServiceHost , cancellationToken : CancellationToken ) : void {
168178 const param = firstOrUndefined ( setAccessorDeclaration . parameters ) ;
169179 if ( param && isIdentifier ( setAccessorDeclaration . name ) && isIdentifier ( param . name ) ) {
170180 const type = inferTypeForVariableFromUsage ( setAccessorDeclaration . name , program , cancellationToken ) ||
171181 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+ }
173188 }
174189 }
175190
176191 function annotate ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , declaration : textChanges . TypeAnnotatable , type : Type | undefined , program : Program , host : LanguageServiceHost ) : void {
177192 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+ }
179201 }
180202
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 {
182213 const checker = program . getTypeChecker ( ) ;
183214 let typeIsAccessible = true ;
184215 const notAccessible = ( ) => { typeIsAccessible = false ; } ;
@@ -212,7 +243,7 @@ namespace ts.codefix {
212243 return InferFromReference . inferTypeFromReferences ( getReferences ( token , program , cancellationToken ) , program . getTypeChecker ( ) , cancellationToken ) ;
213244 }
214245
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 {
216247 switch ( containingFunction . kind ) {
217248 case SyntaxKind . Constructor :
218249 case SyntaxKind . FunctionExpression :
@@ -228,6 +259,13 @@ namespace ts.codefix {
228259 }
229260 }
230261
262+ interface ParameterInference {
263+ declaration : ParameterDeclaration ;
264+ type ?: Type ;
265+ typeNode ?: TypeNode ;
266+ isOptional ?: boolean ;
267+ }
268+
231269 namespace InferFromReference {
232270 interface CallContext {
233271 argumentTypes : Type [ ] ;
@@ -255,7 +293,7 @@ namespace ts.codefix {
255293 return getTypeFromUsageContext ( usageContext , checker ) ;
256294 }
257295
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 {
259297 if ( references . length === 0 ) {
260298 return undefined ;
261299 }
@@ -274,8 +312,10 @@ namespace ts.codefix {
274312 return callContexts && declaration . parameters . map ( ( parameter , parameterIndex ) => {
275313 const types : Type [ ] = [ ] ;
276314 const isRest = isRestParameter ( parameter ) ;
315+ let isOptional = false ;
277316 for ( const callContext of callContexts ) {
278317 if ( callContext . argumentTypes . length <= parameterIndex ) {
318+ isOptional = isInJSFile ( declaration ) ;
279319 continue ;
280320 }
281321
@@ -289,10 +329,14 @@ namespace ts.codefix {
289329 }
290330 }
291331 if ( ! types . length ) {
292- return undefined ;
332+ return { declaration : parameter } ;
293333 }
294334 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+ } ;
296340 } ) ;
297341 }
298342
0 commit comments