cleanup
This commit is contained in:
parent
9110461294
commit
cf3b4d6b00
|
@ -2205,7 +2205,7 @@ namespace FourSlash {
|
|||
this.raiseError(`Should find at least ${index + 1} codefix(es), but ${actions ? actions.length : "none"} found.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const fileChanges = ts.find(actions[index].changes, change => change.fileName === fileName);
|
||||
if (!fileChanges) {
|
||||
this.raiseError("The CodeFix found doesn't provide any changes in this file.");
|
||||
|
|
|
@ -1,98 +1,67 @@
|
|||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code],
|
||||
getCodeActions: getActionsForAddMissingMember
|
||||
});
|
||||
|
||||
function getActionsForAddMissingMember(context: CodeFixContext): CodeAction[] | undefined {
|
||||
|
||||
const sourceFile = context.sourceFile;
|
||||
const start = context.span.start;
|
||||
// This is the identifier in the case of a class declaration
|
||||
// or the class keyword token in the case of a class expression.
|
||||
const token = getTokenAtPosition(sourceFile, start);
|
||||
|
||||
const classDeclaration = getContainingClass(token);
|
||||
if (!classDeclaration) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const startPos = classDeclaration.members.pos;
|
||||
|
||||
if (!(token.parent && token.parent.kind === SyntaxKind.PropertyAccessExpression)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if ((token.parent as PropertyAccessExpression).expression.kind !== SyntaxKind.ThisKeyword) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// if function call, synthesize function declaration
|
||||
if(token.parent.parent.kind == SyntaxKind.CallExpression) {
|
||||
const callExpression = token.parent.parent as CallExpression;
|
||||
if(callExpression.typeArguments) {
|
||||
/**
|
||||
* We can't in general know which arguments should use the type of the expression
|
||||
* or the type of the type argument in the declaration. Consider
|
||||
* ```
|
||||
* class A {
|
||||
* constructor(a: number){
|
||||
* this.foo<number>(a,1,true);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
let typeString: string = 'any';
|
||||
|
||||
// if binary expression, try to infer type for LHS, else use any
|
||||
if (token.parent.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
const binaryExpression = token.parent.parent as BinaryExpression;
|
||||
binaryExpression.operatorToken;
|
||||
|
||||
const checker = context.program.getTypeChecker();
|
||||
const widenedType = checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(binaryExpression.right));
|
||||
typeString = checker.typeToString(widenedType);
|
||||
}
|
||||
|
||||
return [{
|
||||
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_accessor_for_missing_property_0), [token.getText()]),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: startPos, length: 0 },
|
||||
newText: `[name: string]: ${typeString};`
|
||||
}]
|
||||
}]
|
||||
}];
|
||||
}
|
||||
|
||||
// Want to infer type of x when possible. ie:
|
||||
// * assignment,
|
||||
// * function call argument: foo<T>(this.x) where foo(x: SomeType<T>)
|
||||
// * expression with a type assertion: this.x as MyFavoriteType
|
||||
// * access expression: this.x.push("asdf") ... probably an array?
|
||||
// *
|
||||
// What if there are multiple usages of this.x? Create intersection over all usages?
|
||||
|
||||
// needs to be in a class
|
||||
// inferred type might be error. then add any.
|
||||
// either make indexable of the inferred type
|
||||
// add named member of the inferred type.
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code],
|
||||
getCodeActions: getActionsForAddMissingMember
|
||||
});
|
||||
|
||||
function getActionsForAddMissingMember(context: CodeFixContext): CodeAction[] | undefined {
|
||||
|
||||
const sourceFile = context.sourceFile;
|
||||
const start = context.span.start;
|
||||
// This is the identifier of the missing property. eg:
|
||||
// this.missing = 1;
|
||||
// ^^^^^^^
|
||||
const token = getTokenAtPosition(sourceFile, start);
|
||||
|
||||
if (token.kind != SyntaxKind.Identifier) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const classDeclaration = getContainingClass(token);
|
||||
if (!classDeclaration) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!(token.parent && token.parent.kind === SyntaxKind.PropertyAccessExpression)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if ((token.parent as PropertyAccessExpression).expression.kind !== SyntaxKind.ThisKeyword) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let typeString = "any";
|
||||
|
||||
if (token.parent.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
const binaryExpression = token.parent.parent as BinaryExpression;
|
||||
|
||||
const checker = context.program.getTypeChecker();
|
||||
const widenedType = checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(binaryExpression.right));
|
||||
typeString = checker.typeToString(widenedType);
|
||||
}
|
||||
|
||||
const startPos = classDeclaration.members.pos;
|
||||
|
||||
return [{
|
||||
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_accessor_for_missing_property_0), [token.getText()]),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: startPos, length: 0 },
|
||||
newText: `[name: string]: ${typeString};`
|
||||
}]
|
||||
}]
|
||||
}];
|
||||
}
|
||||
}
|
|
@ -58,7 +58,7 @@ namespace ts.codefix {
|
|||
if (declarations.length === 1) {
|
||||
Debug.assert(signatures.length === 1);
|
||||
const sigString = checker.signatureToString(signatures[0], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
|
||||
return getStubbedMethod(visibility, name, sigString, newlineChar);
|
||||
}
|
||||
|
||||
let result = "";
|
||||
|
@ -78,7 +78,7 @@ namespace ts.codefix {
|
|||
bodySig = createBodySignatureWithAnyTypes(signatures, enclosingDeclaration, checker);
|
||||
}
|
||||
const sigString = checker.signatureToString(bodySig, enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
result += `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
|
||||
result += getStubbedMethod(visibility, name, sigString, newlineChar);
|
||||
|
||||
return result;
|
||||
default:
|
||||
|
@ -138,8 +138,8 @@ namespace ts.codefix {
|
|||
}
|
||||
}
|
||||
|
||||
export function getStubbedMethod(visibility: string, name: string, signature: string = '()', newlineChar: string): string {
|
||||
return `${visibility}${name}${signature}${getMethodBodyStub(newlineChar)}`;
|
||||
export function getStubbedMethod(visibility: string, name: string, sigString = "()", newlineChar: string): string {
|
||||
return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
|
||||
}
|
||||
|
||||
function getMethodBodyStub(newlineChar: string) {
|
||||
|
|
Loading…
Reference in a new issue