1- /* @internal */
2- namespace ts . codefix {
3- registerCodeFix ( {
4- errorCodes : [ Diagnostics . Property_0_does_not_exist_on_type_1 . code ] ,
5- getCodeActions : getActionsForAddMissingMember
6- } ) ;
7-
8- function getActionsForAddMissingMember ( context : CodeFixContext ) : CodeAction [ ] | undefined {
9-
10- const sourceFile = context . sourceFile ;
11- const start = context . span . start ;
12- // This is the identifier in the case of a class declaration
13- // or the class keyword token in the case of a class expression.
14- const token = getTokenAtPosition ( sourceFile , start ) ;
15-
16- const classDeclaration = getContainingClass ( token ) ;
17- if ( ! classDeclaration ) {
18- return undefined ;
19- }
20-
21- const startPos = classDeclaration . members . pos ;
22-
23- if ( ! ( token . parent && token . parent . kind === SyntaxKind . PropertyAccessExpression ) ) {
24- return undefined ;
25- }
26-
27- if ( ( token . parent as PropertyAccessExpression ) . expression . kind !== SyntaxKind . ThisKeyword ) {
28- return undefined ;
29- }
30-
31- // if function call, synthesize function declaration
32- if ( token . parent . parent . kind == SyntaxKind . CallExpression ) {
33- const callExpression = token . parent . parent as CallExpression ;
34- if ( callExpression . typeArguments ) {
35- /**
36- * We can't in general know which arguments should use the type of the expression
37- * or the type of the type argument in the declaration. Consider
38- * ```
39- * class A {
40- * constructor(a: number){
41- * this.foo<number>(a,1,true);
42- * }
43- * }
44- * ```
45- */
46- return undefined ;
47- }
48-
49-
50- }
51-
52- let typeString : string = 'any' ;
53-
54- // if binary expression, try to infer type for LHS, else use any
55- if ( token . parent . parent . kind === SyntaxKind . BinaryExpression ) {
56- const binaryExpression = token . parent . parent as BinaryExpression ;
57- binaryExpression . operatorToken ;
58-
59- const checker = context . program . getTypeChecker ( ) ;
60- const widenedType = checker . getBaseTypeOfLiteralType ( checker . getTypeAtLocation ( binaryExpression . right ) ) ;
61- typeString = checker . typeToString ( widenedType ) ;
62- }
63-
64- return [ {
65- description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_declaration_for_missing_property_0 ) , [ token . getText ( ) ] ) ,
66- changes : [ {
67- fileName : sourceFile . fileName ,
68- textChanges : [ {
69- span : { start : startPos , length : 0 } ,
70- newText : `${ token . getFullText ( sourceFile ) } : ${ typeString } ;`
71- } ]
72- } ]
73- } ,
74- {
75- description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_index_accessor_for_missing_property_0 ) , [ token . getText ( ) ] ) ,
76- changes : [ {
77- fileName : sourceFile . fileName ,
78- textChanges : [ {
79- span : { start : startPos , length : 0 } ,
80- newText : `[name: string]: ${ typeString } ;`
81- } ]
82- } ]
83- } ] ;
84- }
85-
86- // Want to infer type of x when possible. ie:
87- // * assignment,
88- // * function call argument: foo<T>(this.x) where foo(x: SomeType<T>)
89- // * expression with a type assertion: this.x as MyFavoriteType
90- // * access expression: this.x.push("asdf") ... probably an array?
91- // *
92- // What if there are multiple usages of this.x? Create intersection over all usages?
93-
94- // needs to be in a class
95- // inferred type might be error. then add any.
96- // either make indexable of the inferred type
97- // add named member of the inferred type.
1+ /* @internal */
2+ namespace ts . codefix {
3+ registerCodeFix ( {
4+ errorCodes : [ Diagnostics . Property_0_does_not_exist_on_type_1 . code ] ,
5+ getCodeActions : getActionsForAddMissingMember
6+ } ) ;
7+
8+ function getActionsForAddMissingMember ( context : CodeFixContext ) : CodeAction [ ] | undefined {
9+
10+ const sourceFile = context . sourceFile ;
11+ const start = context . span . start ;
12+ // This is the identifier of the missing property. eg:
13+ // this.missing = 1;
14+ // ^^^^^^^
15+ const token = getTokenAtPosition ( sourceFile , start ) ;
16+
17+ if ( token . kind != SyntaxKind . Identifier ) {
18+ return undefined ;
19+ }
20+
21+ const classDeclaration = getContainingClass ( token ) ;
22+ if ( ! classDeclaration ) {
23+ return undefined ;
24+ }
25+
26+ if ( ! ( token . parent && token . parent . kind === SyntaxKind . PropertyAccessExpression ) ) {
27+ return undefined ;
28+ }
29+
30+ if ( ( token . parent as PropertyAccessExpression ) . expression . kind !== SyntaxKind . ThisKeyword ) {
31+ return undefined ;
32+ }
33+
34+ let typeString = "any" ;
35+
36+ if ( token . parent . parent . kind === SyntaxKind . BinaryExpression ) {
37+ const binaryExpression = token . parent . parent as BinaryExpression ;
38+
39+ const checker = context . program . getTypeChecker ( ) ;
40+ const widenedType = checker . getBaseTypeOfLiteralType ( checker . getTypeAtLocation ( binaryExpression . right ) ) ;
41+ typeString = checker . typeToString ( widenedType ) ;
42+ }
43+
44+ const startPos = classDeclaration . members . pos ;
45+
46+ return [ {
47+ description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_declaration_for_missing_property_0 ) , [ token . getText ( ) ] ) ,
48+ changes : [ {
49+ fileName : sourceFile . fileName ,
50+ textChanges : [ {
51+ span : { start : startPos , length : 0 } ,
52+ newText : `${ token . getFullText ( sourceFile ) } : ${ typeString } ;`
53+ } ]
54+ } ]
55+ } ,
56+ {
57+ description : formatStringFromArgs ( getLocaleSpecificMessage ( Diagnostics . Add_index_accessor_for_missing_property_0 ) , [ token . getText ( ) ] ) ,
58+ changes : [ {
59+ fileName : sourceFile . fileName ,
60+ textChanges : [ {
61+ span : { start : startPos , length : 0 } ,
62+ newText : `[name: string]: ${ typeString } ;`
63+ } ]
64+ } ]
65+ } ] ;
66+ }
9867}
0 commit comments