Support static properties

This commit is contained in:
Mohamed Hegazy 2017-03-13 22:49:54 -07:00
parent 6e86596a73
commit fd9fb8f9bc
9 changed files with 181 additions and 36 deletions

View file

@ -1,4 +1,4 @@
/* @internal */
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code],
@ -13,22 +13,27 @@ namespace ts.codefix {
// this.missing = 1;
// ^^^^^^^
const token = getTokenAtPosition(sourceFile, start);
if (token.kind != SyntaxKind.Identifier) {
return undefined;
}
const classDeclaration = getContainingClass(token);
if (!classDeclaration) {
return undefined;
}
if (!isPropertyAccessExpression(token.parent) || token.parent.expression.kind !== SyntaxKind.ThisKeyword) {
return undefined;
}
return isInJavaScriptFile(sourceFile) ? getActionsForAddMissingMemberInJavaScriptFile() : getActionsForAddMissingMemberInTypeScriptFile();
const classMemberDeclaration = getThisContainer(token, /*includeArrowFunctions*/ false);
if (!isClassElement(classMemberDeclaration)) {
return undefined;
}
const classDeclaration = <ClassLikeDeclaration>classMemberDeclaration.parent;
if (!classDeclaration || !isClassLike(classDeclaration)) {
return undefined;
}
const isStatic = hasModifier(getThisContainer(token, /*includeArrowFunctions*/ false), ModifierFlags.Static);
return isInJavaScriptFile(sourceFile) ? getActionsForAddMissingMemberInJavaScriptFile() : getActionsForAddMissingMemberInTypeScriptFile();
function getActionsForAddMissingMemberInTypeScriptFile(): CodeAction[] | undefined {
let typeString = "any";
@ -43,47 +48,71 @@ namespace ts.codefix {
const startPos = classDeclaration.members.pos;
return [{
const actions = [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [token.getText()]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: startPos, length: 0 },
newText: `${token.getFullText(sourceFile)}: ${typeString};`
}]
}]
},
{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_missing_property_0), [token.getText()]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: startPos, length: 0 },
newText: `[name: string]: ${typeString};`
newText: `${isStatic ? "static " : ""}${token.getFullText(sourceFile)}: ${typeString};`
}]
}]
}];
if (!isStatic) {
actions.push({
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_missing_property_0), [token.getText()]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: startPos, length: 0 },
newText: `[x: string]: ${typeString};`
}]
}]
});
}
return actions;
}
function getActionsForAddMissingMemberInJavaScriptFile(): CodeAction[] | undefined {
const classConstructor = getFirstConstructorWithBody(classDeclaration);
if (!classConstructor) {
return undefined;
}
const memberName = token.getText();
const startPos = classConstructor.body.getEnd() - 1;
return [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_property_0_in_the_constructor), [memberName]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: startPos, length: 0 },
newText: `this.${memberName} = undefined;`
if (isStatic) {
if (classDeclaration.kind === SyntaxKind.ClassExpression) {
return undefined;
}
const className = classDeclaration.name.getText();
return [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_static_property_0), [memberName]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: classDeclaration.getEnd(), length: 0 },
newText: `${context.newLineCharacter}${className}.${memberName} = undefined;${context.newLineCharacter}`
}]
}]
}]
}];
}];
}
else {
const classConstructor = getFirstConstructorWithBody(classDeclaration);
if (!classConstructor) {
return undefined;
}
return [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_property_0_in_the_constructor), [memberName]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: classConstructor.body.getEnd() - 1, length: 0 },
newText: `this.${memberName} = undefined;${context.newLineCharacter}`
}]
}]
}];
}
}
}
}

View file

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
////[|class C {
//// method() {
//// this.foo = 10;
//// }
////}|]
verify.rangeAfterCodeFix(`class C {
foo: number;
method() {
this.foo = 10;
}
}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);

View file

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
////[|class C {
//// method() {
//// this.foo = 10;
//// }
////}|]
verify.rangeAfterCodeFix(`class C {
[x:string]: number;
method() {
this.foo = 10;
}
}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 1);

View file

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
////[|class C {
//// static method() {
//// this.foo = 10;
//// }
////}|]
verify.rangeAfterCodeFix(`class C {
static foo: number;
static method() {
this.foo = 10;
}
}`);

View file

@ -0,0 +1,22 @@
/// <reference path='fourslash.ts' />
// @checkJs: true
// @allowJs: true
// @Filename: a.js
////[|class C {
//// constructor() {
//// }
//// method() {
//// this.foo === 10;
//// }
////}|]
verify.rangeAfterCodeFix(`class C {
constructor() {
this.foo = undefined;
}
method() {
this.foo === 10;
}
}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);

View file

@ -0,0 +1,19 @@
/// <reference path='fourslash.ts' />
// @checkJs: true
// @allowJs: true
// @Filename: a.js
////[|class C {
//// static method() {
//// ()=>{ this.foo === 10 };
//// }
////}
////|]
verify.rangeAfterCodeFix(`class C {
static method() {
()=>{ this.foo === 10 };
}
}
C.foo = undefined;`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);

View file

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
// @checkJs: true
// @allowJs: true
// @Filename: a.js
////[|class C {
//// constructor() {
//// }
//// prop = ()=>{ this.foo === 10 };
////}|]
verify.rangeAfterCodeFix(`class C {
constructor() {
this.foo = undefined;
}
prop = ()=>{ this.foo === 10 };
}`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);

View file

@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
// @checkJs: true
// @allowJs: true
// @Filename: a.js
////[|class C {
//// static p = ()=>{ this.foo === 10 };
////}
////|]
verify.rangeAfterCodeFix(`class C {
static p = ()=>{ this.foo === 10 };
}
C.foo = undefined;`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 2);

View file

@ -8,7 +8,7 @@
verify.rangeAfterCodeFix(`
class A {
[name: string]: number;
[x: string]: number;
constructor() {
this.x = 10;