@@ -8,116 +8,151 @@ namespace ts.codefix {
88
99 function getActionsForAddMissingMember ( context : CodeFixContext ) : CodeAction [ ] | undefined {
1010
11- const sourceFile = context . sourceFile ;
11+ const tokenSourceFile = context . sourceFile ;
1212 const start = context . span . start ;
13- // This is the identifier of the missing property. eg:
13+ // The identifier of the missing property. eg:
1414 // this.missing = 1;
1515 // ^^^^^^^
16- const token = getTokenAtPosition ( sourceFile , start , /*includeJsDocComment*/ false ) ;
16+ const token = getTokenAtPosition ( tokenSourceFile , start , /*includeJsDocComment*/ false ) ;
1717
1818 if ( token . kind !== SyntaxKind . Identifier ) {
1919 return undefined ;
2020 }
2121
22- if ( ! isPropertyAccessExpression ( token . parent ) || token . parent . expression . kind !== SyntaxKind . ThisKeyword ) {
22+ if ( ! isPropertyAccessExpression ( token . parent ) ) {
2323 return undefined ;
2424 }
2525
26- const classMemberDeclaration = getThisContainer ( token , /*includeArrowFunctions*/ false ) ;
27- if ( ! isClassElement ( classMemberDeclaration ) ) {
28- return undefined ;
26+ const tokenName = token . getText ( tokenSourceFile ) ;
27+
28+ let makeStatic = false ;
29+ let classDeclaration : ClassLikeDeclaration ;
30+
31+ if ( token . parent . expression . kind === SyntaxKind . ThisKeyword ) {
32+ const containingClassMemberDeclaration = getThisContainer ( token , /*includeArrowFunctions*/ false ) ;
33+ if ( ! isClassElement ( containingClassMemberDeclaration ) ) {
34+ return undefined ;
35+ }
36+
37+ classDeclaration = < ClassLikeDeclaration > containingClassMemberDeclaration . parent ;
38+
39+ // Property accesses on `this` in a static method are accesses of a static member.
40+ makeStatic = classDeclaration && hasModifier ( containingClassMemberDeclaration , ModifierFlags . Static ) ;
41+ }
42+ else {
43+
44+ const checker = context . program . getTypeChecker ( ) ;
45+ const leftExpression = token . parent . expression ;
46+ const leftExpressionType = checker . getTypeAtLocation ( leftExpression ) ;
47+
48+ if ( leftExpressionType . flags & TypeFlags . Object ) {
49+ const symbol = leftExpressionType . symbol ;
50+ if ( symbol . flags & SymbolFlags . Class ) {
51+ classDeclaration = symbol . declarations && < ClassLikeDeclaration > symbol . declarations [ 0 ] ;
52+ if ( getObjectFlags ( leftExpressionType ) & ObjectFlags . Anonymous && symbol . flags & SymbolFlags . Class && ! checker . getBaseTypeVariableOfClass ( symbol ) ) {
53+ makeStatic = true ;
54+ }
55+ }
56+ }
2957 }
3058
31- const classDeclaration = < ClassLikeDeclaration > classMemberDeclaration . parent ;
3259 if ( ! classDeclaration || ! isClassLike ( classDeclaration ) ) {
3360 return undefined ;
3461 }
3562
36- const tokenName = token . getText ( sourceFile ) ;
37- const isStatic = hasModifier ( classMemberDeclaration , ModifierFlags . Static ) ;
63+ const classDeclarationSourceFile = getSourceFileOfNode ( classDeclaration ) ;
64+ const classOpenBrace = getOpenBraceOfClassLike ( classDeclaration , classDeclarationSourceFile ) ;
3865
39- return isInJavaScriptFile ( sourceFile ) ? getActionsForAddMissingMemberInJavaScriptFile ( ) : getActionsForAddMissingMemberInTypeScriptFile ( ) ;
66+ return isInJavaScriptFile ( classDeclarationSourceFile ) ?
67+ getActionsForAddMissingMemberInJavaScriptFile ( classDeclaration , makeStatic ) :
68+ getActionsForAddMissingMemberInTypeScriptFile ( classDeclaration , makeStatic ) ;
69+
70+ function getActionsForAddMissingMemberInJavaScriptFile ( classDeclaration : ClassLikeDeclaration , makeStatic : boolean ) : CodeAction [ ] | undefined {
71+ let actions : CodeAction [ ] ;
4072
41- function getActionsForAddMissingMemberInJavaScriptFile ( ) : CodeAction [ ] | undefined {
42- if ( isStatic ) {
73+ const methodCodeAction = getActionForMethodDeclaration ( ) ;
74+ if ( methodCodeAction ) {
75+ actions = [ methodCodeAction ] ;
76+ }
77+
78+ if ( makeStatic ) {
4379 if ( classDeclaration . kind === SyntaxKind . ClassExpression ) {
44- return undefined ;
80+ return actions ;
4581 }
4682
4783 const className = classDeclaration . name . getText ( ) ;
4884
49- return [ {
85+ const initializeStaticAction = {
5086 description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Initialize_static_property_0 ) , [ tokenName ] ) ,
5187 changes : [ {
52- fileName : sourceFile . fileName ,
88+ fileName : classDeclarationSourceFile . fileName ,
5389 textChanges : [ {
5490 span : { start : classDeclaration . getEnd ( ) , length : 0 } ,
5591 newText : `${ context . newLineCharacter } ${ className } .${ tokenName } = undefined;${ context . newLineCharacter } `
5692 } ]
5793 } ]
58- } ] ;
94+ } ;
5995
96+ ( actions || ( actions = [ ] ) ) . push ( initializeStaticAction ) ;
97+ return actions ;
6098 }
6199 else {
62100 const classConstructor = getFirstConstructorWithBody ( classDeclaration ) ;
63101 if ( ! classConstructor ) {
64- return undefined ;
102+ return actions ;
65103 }
66104
67- return [ {
105+ const initializeAction = {
68106 description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Initialize_property_0_in_the_constructor ) , [ tokenName ] ) ,
69107 changes : [ {
70- fileName : sourceFile . fileName ,
108+ fileName : classDeclarationSourceFile . fileName ,
71109 textChanges : [ {
72110 span : { start : classConstructor . body . getEnd ( ) - 1 , length : 0 } ,
73111 newText : `this.${ tokenName } = undefined;${ context . newLineCharacter } `
74112 } ]
75113 } ]
76- } ] ;
114+ } ;
115+
116+ ( actions || ( actions = [ ] ) ) . push ( initializeAction ) ;
117+ return actions ;
77118 }
78119 }
79120
80- function getActionsForAddMissingMemberInTypeScriptFile ( ) : CodeAction [ ] | undefined {
81- const openBrace = getOpenBraceOfClassLike ( classDeclaration , sourceFile ) ;
121+ function getActionsForAddMissingMemberInTypeScriptFile ( classDeclaration : ClassLikeDeclaration , makeStatic : boolean ) : CodeAction [ ] | undefined {
82122 let actions : CodeAction [ ] ;
83123
84- if ( token . parent . parent . kind === SyntaxKind . CallExpression ) {
85- const callExpression = < CallExpression > token . parent . parent ;
86- const methodDeclaration = createMethodFromCallExpression ( callExpression , tokenName ) ;
87-
88- const methodDeclarationChangeTracker = textChanges . ChangeTracker . fromCodeFixContext ( context ) ;
89- methodDeclarationChangeTracker . insertNodeAfter ( sourceFile , openBrace , methodDeclaration , { suffix : context . newLineCharacter } ) ;
90- actions = [ {
91- description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_declaration_for_missing_method_0 ) , [ tokenName ] ) ,
92- changes : methodDeclarationChangeTracker . getChanges ( )
93- } ] ;
124+ const methodCodeAction = getActionForMethodDeclaration ( ) ;
125+ if ( methodCodeAction ) {
126+ actions = [ methodCodeAction ] ;
94127 }
95128
96129 let typeNode : TypeNode ;
97130 if ( token . parent . parent . kind === SyntaxKind . BinaryExpression ) {
98131 const binaryExpression = token . parent . parent as BinaryExpression ;
132+ const otherExpression = token . parent === binaryExpression . left ? binaryExpression . right : binaryExpression . left ;
99133 const checker = context . program . getTypeChecker ( ) ;
100- const widenedType = checker . getWidenedType ( checker . getBaseTypeOfLiteralType ( checker . getTypeAtLocation ( binaryExpression . right ) ) ) ;
134+ const widenedType = checker . getWidenedType ( checker . getBaseTypeOfLiteralType ( checker . getTypeAtLocation ( otherExpression ) ) ) ;
101135 typeNode = checker . typeToTypeNode ( widenedType , classDeclaration ) ;
102136 }
103137 typeNode = typeNode || createKeywordTypeNode ( SyntaxKind . AnyKeyword ) ;
104138
105139 const property = createProperty (
106140 /*decorators*/ undefined ,
107- /*modifiers*/ isStatic ? [ createToken ( SyntaxKind . StaticKeyword ) ] : undefined ,
141+ /*modifiers*/ makeStatic ? [ createToken ( SyntaxKind . StaticKeyword ) ] : undefined ,
108142 tokenName ,
109143 /*questionToken*/ undefined ,
110144 typeNode ,
111145 /*initializer*/ undefined ) ;
112146 const propertyChangeTracker = textChanges . ChangeTracker . fromCodeFixContext ( context ) ;
113- propertyChangeTracker . insertNodeAfter ( sourceFile , openBrace , property , { suffix : context . newLineCharacter } ) ;
147+ propertyChangeTracker . insertNodeAfter ( classDeclarationSourceFile , classOpenBrace , property , { suffix : context . newLineCharacter } ) ;
114148
115149 ( actions || ( actions = [ ] ) ) . push ( {
116- description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_declaration_for_missing_property_0 ) , [ tokenName ] ) ,
150+ description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Declare_property_0 ) , [ tokenName ] ) ,
117151 changes : propertyChangeTracker . getChanges ( )
118152 } ) ;
119153
120- if ( ! isStatic ) {
154+ if ( ! makeStatic ) {
155+ // Index signatures cannot have the static modifier.
121156 const stringTypeNode = createKeywordTypeNode ( SyntaxKind . StringKeyword ) ;
122157 const indexingParameter = createParameter (
123158 /*decorators*/ undefined ,
@@ -134,15 +169,32 @@ namespace ts.codefix {
134169 typeNode ) ;
135170
136171 const indexSignatureChangeTracker = textChanges . ChangeTracker . fromCodeFixContext ( context ) ;
137- indexSignatureChangeTracker . insertNodeAfter ( sourceFile , openBrace , indexSignature , { suffix : context . newLineCharacter } ) ;
172+ indexSignatureChangeTracker . insertNodeAfter ( classDeclarationSourceFile , classOpenBrace , indexSignature , { suffix : context . newLineCharacter } ) ;
138173
139174 actions . push ( {
140- description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_index_signature_for_missing_property_0 ) , [ tokenName ] ) ,
175+ description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_index_signature_for_property_0 ) , [ tokenName ] ) ,
141176 changes : indexSignatureChangeTracker . getChanges ( )
142177 } ) ;
143178 }
144179
145180 return actions ;
146181 }
182+
183+ function getActionForMethodDeclaration ( ) : CodeAction | undefined {
184+ if ( token . parent . parent . kind === SyntaxKind . CallExpression ) {
185+ const callExpression = < CallExpression > token . parent . parent ;
186+ const methodDeclaration = createMethodFromCallExpression ( callExpression , tokenName , /*includeTypeScriptSyntax*/ true , makeStatic ) ;
187+
188+ const methodDeclarationChangeTracker = textChanges . ChangeTracker . fromCodeFixContext ( context ) ;
189+ methodDeclarationChangeTracker . insertNodeAfter ( classDeclarationSourceFile , classOpenBrace , methodDeclaration , { suffix : context . newLineCharacter } ) ;
190+ return {
191+ description : formatStringFromArgs ( getLocaleSpecificMessage ( makeStatic ?
192+ Diagnostics . Declare_method_0 :
193+ Diagnostics . Declare_static_method_0 ) ,
194+ [ tokenName ] ) ,
195+ changes : methodDeclarationChangeTracker . getChanges ( )
196+ } ;
197+ }
198+ }
147199 }
148200}
0 commit comments