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
+ }
98
67
}
0 commit comments