@@ -11,14 +11,15 @@ namespace ts.codefix {
11
11
Diagnostics . Property_0_is_missing_in_type_1_but_required_in_type_2 . code ,
12
12
Diagnostics . Type_0_is_missing_the_following_properties_from_type_1_Colon_2 . code ,
13
13
Diagnostics . Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more . code ,
14
+ Diagnostics . Argument_of_type_0_is_not_assignable_to_parameter_of_type_1 . code ,
14
15
Diagnostics . Cannot_find_name_0 . code
15
16
] ;
16
17
17
18
registerCodeFix ( {
18
19
errorCodes,
19
20
getCodeActions ( context ) {
20
21
const typeChecker = context . program . getTypeChecker ( ) ;
21
- const info = getInfo ( context . sourceFile , context . span . start , typeChecker , context . program ) ;
22
+ const info = getInfo ( context . sourceFile , context . span . start , context . errorCode , typeChecker , context . program ) ;
22
23
if ( ! info ) {
23
24
return undefined ;
24
25
}
@@ -49,7 +50,7 @@ namespace ts.codefix {
49
50
50
51
return createCombinedCodeActions ( textChanges . ChangeTracker . with ( context , changes => {
51
52
eachDiagnostic ( context , errorCodes , diag => {
52
- const info = getInfo ( diag . file , diag . start , checker , context . program ) ;
53
+ const info = getInfo ( diag . file , diag . start , diag . code , checker , context . program ) ;
53
54
if ( ! info || ! addToSeen ( seen , getNodeId ( info . parentDeclaration ) + "#" + info . token . text ) ) {
54
55
return ;
55
56
}
@@ -139,6 +140,7 @@ namespace ts.codefix {
139
140
readonly token : Identifier ;
140
141
readonly properties : Symbol [ ] ;
141
142
readonly parentDeclaration : ObjectLiteralExpression ;
143
+ readonly indentation ?: number ;
142
144
}
143
145
144
146
interface JsxAttributesInfo {
@@ -148,43 +150,53 @@ namespace ts.codefix {
148
150
readonly parentDeclaration : JsxOpeningLikeElement ;
149
151
}
150
152
151
- function getInfo ( sourceFile : SourceFile , tokenPos : number , checker : TypeChecker , program : Program ) : Info | undefined {
153
+ function getInfo ( sourceFile : SourceFile , tokenPos : number , errorCode : number , checker : TypeChecker , program : Program ) : Info | undefined {
152
154
// The identifier of the missing property. eg:
153
155
// this.missing = 1;
154
156
// ^^^^^^^
155
157
const token = getTokenAtPosition ( sourceFile , tokenPos ) ;
156
- if ( ! isIdentifier ( token ) && ! isPrivateIdentifier ( token ) ) {
157
- return undefined ;
158
+ const parent = token . parent ;
159
+
160
+ if ( errorCode === Diagnostics . Argument_of_type_0_is_not_assignable_to_parameter_of_type_1 . code ) {
161
+ if ( ! ( token . kind === SyntaxKind . OpenBraceToken && isObjectLiteralExpression ( parent ) && isCallExpression ( parent . parent ) ) ) return undefined ;
162
+
163
+ const argIndex = findIndex ( parent . parent . arguments , arg => arg === parent ) ;
164
+ if ( argIndex < 0 ) return undefined ;
165
+
166
+ const signature = singleOrUndefined ( checker . getSignaturesOfType ( checker . getTypeAtLocation ( parent . parent . expression ) , SignatureKind . Call ) ) ;
167
+ if ( ! ( signature && signature . declaration && signature . parameters [ argIndex ] ) ) return undefined ;
168
+
169
+ const param = signature . parameters [ argIndex ] . valueDeclaration ;
170
+ if ( ! ( param && isParameter ( param ) && isIdentifier ( param . name ) ) ) return undefined ;
171
+
172
+ const properties = arrayFrom ( checker . getUnmatchedProperties ( checker . getTypeAtLocation ( parent ) , checker . getTypeAtLocation ( param ) , /* requireOptionalProperties */ false , /* matchDiscriminantProperties */ false ) ) ;
173
+ if ( ! length ( properties ) ) return undefined ;
174
+ return { kind : InfoKind . ObjectLiteral , token : param . name , properties, indentation : 0 , parentDeclaration : parent } ;
158
175
}
159
176
160
- const { parent } = token ;
177
+ if ( ! isMemberName ( token ) ) return undefined ;
178
+
161
179
if ( isIdentifier ( token ) && hasInitializer ( parent ) && parent . initializer && isObjectLiteralExpression ( parent . initializer ) ) {
162
180
const properties = arrayFrom ( checker . getUnmatchedProperties ( checker . getTypeAtLocation ( parent . initializer ) , checker . getTypeAtLocation ( token ) , /* requireOptionalProperties */ false , /* matchDiscriminantProperties */ false ) ) ;
163
- if ( length ( properties ) ) {
164
- return { kind : InfoKind . ObjectLiteral , token, properties, parentDeclaration : parent . initializer } ;
165
- }
181
+ if ( ! length ( properties ) ) return undefined ;
182
+ return { kind : InfoKind . ObjectLiteral , token, properties, indentation : undefined , parentDeclaration : parent . initializer } ;
166
183
}
167
184
168
185
if ( isIdentifier ( token ) && isJsxOpeningLikeElement ( token . parent ) ) {
169
186
const attributes = getUnmatchedAttributes ( checker , token . parent ) ;
170
- if ( length ( attributes ) ) {
171
- return { kind : InfoKind . JsxAttributes , token, attributes, parentDeclaration : token . parent } ;
172
- }
187
+ if ( ! length ( attributes ) ) return undefined ;
188
+ return { kind : InfoKind . JsxAttributes , token, attributes, parentDeclaration : token . parent } ;
173
189
}
174
190
175
191
if ( isIdentifier ( token ) && isCallExpression ( parent ) ) {
176
192
return { kind : InfoKind . Function , token, call : parent , sourceFile, modifierFlags : ModifierFlags . None , parentDeclaration : sourceFile } ;
177
193
}
178
194
179
- if ( ! isPropertyAccessExpression ( parent ) ) {
180
- return undefined ;
181
- }
195
+ if ( ! isPropertyAccessExpression ( parent ) ) return undefined ;
182
196
183
197
const leftExpressionType = skipConstraint ( checker . getTypeAtLocation ( parent . expression ) ) ;
184
- const { symbol } = leftExpressionType ;
185
- if ( ! symbol || ! symbol . declarations ) {
186
- return undefined ;
187
- }
198
+ const symbol = leftExpressionType . symbol ;
199
+ if ( ! symbol || ! symbol . declarations ) return undefined ;
188
200
189
201
if ( isIdentifier ( token ) && isCallExpression ( parent . parent ) ) {
190
202
const moduleDeclaration = find ( symbol . declarations , isModuleDeclaration ) ;
@@ -194,9 +206,7 @@ namespace ts.codefix {
194
206
}
195
207
196
208
const moduleSourceFile = find ( symbol . declarations , isSourceFile ) ;
197
- if ( sourceFile . commonJsModuleIndicator ) {
198
- return ;
199
- }
209
+ if ( sourceFile . commonJsModuleIndicator ) return undefined ;
200
210
201
211
if ( moduleSourceFile && ! isSourceFileFromLibrary ( program , moduleSourceFile ) ) {
202
212
return { kind : InfoKind . Function , token, call : parent . parent , sourceFile : moduleSourceFile , modifierFlags : ModifierFlags . Export , parentDeclaration : moduleSourceFile } ;
@@ -205,17 +215,13 @@ namespace ts.codefix {
205
215
206
216
const classDeclaration = find ( symbol . declarations , isClassLike ) ;
207
217
// Don't suggest adding private identifiers to anything other than a class.
208
- if ( ! classDeclaration && isPrivateIdentifier ( token ) ) {
209
- return undefined ;
210
- }
218
+ if ( ! classDeclaration && isPrivateIdentifier ( token ) ) return undefined ;
211
219
212
220
// Prefer to change the class instead of the interface if they are merged
213
221
const classOrInterface = classDeclaration || find ( symbol . declarations , isInterfaceDeclaration ) ;
214
222
if ( classOrInterface && ! isSourceFileFromLibrary ( program , classOrInterface . getSourceFile ( ) ) ) {
215
223
const makeStatic = ( ( leftExpressionType as TypeReference ) . target || leftExpressionType ) !== checker . getDeclaredTypeOfSymbol ( symbol ) ;
216
- if ( makeStatic && ( isPrivateIdentifier ( token ) || isInterfaceDeclaration ( classOrInterface ) ) ) {
217
- return undefined ;
218
- }
224
+ if ( makeStatic && ( isPrivateIdentifier ( token ) || isInterfaceDeclaration ( classOrInterface ) ) ) return undefined ;
219
225
220
226
const declSourceFile = classOrInterface . getSourceFile ( ) ;
221
227
const modifierFlags = ( makeStatic ? ModifierFlags . Static : 0 ) | ( startsWithUnderscore ( token . text ) ? ModifierFlags . Private : 0 ) ;
@@ -475,7 +481,12 @@ namespace ts.codefix {
475
481
const initializer = prop . valueDeclaration ? tryGetValueFromType ( context , checker , importAdder , quotePreference , checker . getTypeAtLocation ( prop . valueDeclaration ) ) : createUndefined ( ) ;
476
482
return factory . createPropertyAssignment ( prop . name , initializer ) ;
477
483
} ) ;
478
- changes . replaceNode ( context . sourceFile , info . parentDeclaration , factory . createObjectLiteralExpression ( [ ...info . parentDeclaration . properties , ...props ] , /*multiLine*/ true ) ) ;
484
+ const options = {
485
+ leadingTriviaOption : textChanges . LeadingTriviaOption . Exclude ,
486
+ trailingTriviaOption : textChanges . TrailingTriviaOption . Exclude ,
487
+ indentation : info . indentation
488
+ } ;
489
+ changes . replaceNode ( context . sourceFile , info . parentDeclaration , factory . createObjectLiteralExpression ( [ ...info . parentDeclaration . properties , ...props ] , /*multiLine*/ true ) , options ) ;
479
490
}
480
491
481
492
function tryGetValueFromType ( context : CodeFixContextBase , checker : TypeChecker , importAdder : ImportAdder , quotePreference : QuotePreference , type : Type ) : Expression {
0 commit comments