Skip to content

Commit a4cf12e

Browse files
author
Arthur Ozga
committed
cleanup
1 parent d9e0fff commit a4cf12e

File tree

3 files changed

+71
-102
lines changed

3 files changed

+71
-102
lines changed

src/harness/fourslash.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2205,7 +2205,7 @@ namespace FourSlash {
22052205
this.raiseError(`Should find at least ${index + 1} codefix(es), but ${actions ? actions.length : "none"} found.`);
22062206
}
22072207
}
2208-
2208+
22092209
const fileChanges = ts.find(actions[index].changes, change => change.fileName === fileName);
22102210
if (!fileChanges) {
22112211
this.raiseError("The CodeFix found doesn't provide any changes in this file.");
Lines changed: 66 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,67 @@
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
}

src/services/codefixes/helpers.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ namespace ts.codefix {
5858
if (declarations.length === 1) {
5959
Debug.assert(signatures.length === 1);
6060
const sigString = checker.signatureToString(signatures[0], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
61-
return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
61+
return getStubbedMethod(visibility, name, sigString, newlineChar);
6262
}
6363

6464
let result = "";
@@ -78,7 +78,7 @@ namespace ts.codefix {
7878
bodySig = createBodySignatureWithAnyTypes(signatures, enclosingDeclaration, checker);
7979
}
8080
const sigString = checker.signatureToString(bodySig, enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
81-
result += `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
81+
result += getStubbedMethod(visibility, name, sigString, newlineChar);
8282

8383
return result;
8484
default:
@@ -138,8 +138,8 @@ namespace ts.codefix {
138138
}
139139
}
140140

141-
export function getStubbedMethod(visibility: string, name: string, signature: string = '()', newlineChar: string): string {
142-
return `${visibility}${name}${signature}${getMethodBodyStub(newlineChar)}`;
141+
export function getStubbedMethod(visibility: string, name: string, sigString = "()", newlineChar: string): string {
142+
return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
143143
}
144144

145145
function getMethodBodyStub(newlineChar: string) {

0 commit comments

Comments
 (0)