diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts
index 7dbbc39843f..2adbfd1dd85 100644
--- a/src/services/codefixes/fixAddMissingMember.ts
+++ b/src/services/codefixes/fixAddMissingMember.ts
@@ -31,7 +31,7 @@ namespace ts.codefix {
// Always prefer to add a method declaration if possible.
if (call) {
- addMethodDeclaration(changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs);
+ addMethodDeclaration(context, changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs);
}
else {
if (inJs) {
@@ -181,14 +181,14 @@ namespace ts.codefix {
return { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]), changes, fixId: undefined };
}
- function getActionForMethodDeclaration(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined {
+ function getActionForMethodDeclaration(context: CodeFixContextBase, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined {
const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0), [token.text]);
- const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs));
+ const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(context, t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs));
return { description, changes, fixId };
}
- function addMethodDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean) {
- const methodDeclaration = createMethodFromCallExpression(callExpression, token.text, inJs, makeStatic);
+ function addMethodDeclaration(context: CodeFixContextBase, changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean) {
+ const methodDeclaration = createMethodFromCallExpression(context, callExpression, token.text, inJs, makeStatic);
changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, methodDeclaration);
}
}
diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts
index 6a45c66ca81..f746a08561c 100644
--- a/src/services/codefixes/helpers.ts
+++ b/src/services/codefixes/helpers.ts
@@ -107,7 +107,18 @@ namespace ts.codefix {
return nodes && createNodeArray(nodes.map(getSynthesizedDeepClone));
}
- export function createMethodFromCallExpression({ typeArguments, arguments: args }: CallExpression, methodName: string, inJs: boolean, makeStatic: boolean): MethodDeclaration {
+ export function createMethodFromCallExpression(context: CodeFixContextBase, { typeArguments, arguments: args }: CallExpression, methodName: string, inJs: boolean, makeStatic: boolean): MethodDeclaration {
+ const checker = context.program.getTypeChecker();
+ const types = map(args,
+ (arg) => {
+ let type = checker.getTypeAtLocation(arg);
+ // Widen the type so we don't emit nonsense annotations like "function fn(x: 3) {"
+ type = checker.getBaseTypeOfLiteralType(type);
+ return checker.typeToTypeNode(type);
+ });
+ const names = map(args, (arg) =>
+ isIdentifier(arg) ? arg.text :
+ isPropertyAccessExpression(arg) ? arg.name.text : undefined);
return createMethod(
/*decorators*/ undefined,
/*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined,
@@ -116,12 +127,12 @@ namespace ts.codefix {
/*questionToken*/ undefined,
/*typeParameters*/ inJs ? undefined : map(typeArguments, (_, i) =>
createTypeParameterDeclaration(CharacterCodes.T + typeArguments.length - 1 <= CharacterCodes.Z ? String.fromCharCode(CharacterCodes.T + i) : `T${i}`)),
- /*parameters*/ createDummyParameters(args.length, /*names*/ undefined, /*minArgumentCount*/ undefined, inJs),
+ /*parameters*/ createDummyParameters(args.length, names, types, /*minArgumentCount*/ undefined, inJs),
/*type*/ inJs ? undefined : createKeywordTypeNode(SyntaxKind.AnyKeyword),
createStubbedMethodBody());
}
- function createDummyParameters(argCount: number, names: string[] | undefined, minArgumentCount: number | undefined, inJs: boolean): ParameterDeclaration[] {
+ function createDummyParameters(argCount: number, names: string[] | undefined, types: TypeNode[], minArgumentCount: number | undefined, inJs: boolean): ParameterDeclaration[] {
const parameters: ParameterDeclaration[] = [];
for (let i = 0; i < argCount; i++) {
const newParameter = createParameter(
@@ -130,7 +141,7 @@ namespace ts.codefix {
/*dotDotDotToken*/ undefined,
/*name*/ names && names[i] || `arg${i}`,
/*questionToken*/ minArgumentCount !== undefined && i >= minArgumentCount ? createToken(SyntaxKind.QuestionToken) : undefined,
- /*type*/ inJs ? undefined : createKeywordTypeNode(SyntaxKind.AnyKeyword),
+ /*type*/ inJs ? undefined : types && types[i] || createKeywordTypeNode(SyntaxKind.AnyKeyword),
/*initializer*/ undefined);
parameters.push(newParameter);
}
@@ -157,7 +168,7 @@ namespace ts.codefix {
const maxNonRestArgs = maxArgsSignature.parameters.length - (maxArgsSignature.hasRestParameter ? 1 : 0);
const maxArgsParameterSymbolNames = maxArgsSignature.parameters.map(symbol => symbol.name);
- const parameters = createDummyParameters(maxNonRestArgs, maxArgsParameterSymbolNames, minArgumentCount, /*inJs*/ false);
+ const parameters = createDummyParameters(maxNonRestArgs, maxArgsParameterSymbolNames, /* types */ undefined, minArgumentCount, /*inJs*/ false);
if (someSigHasRestParameter) {
const anyArrayType = createArrayTypeNode(createKeywordTypeNode(SyntaxKind.AnyKeyword));
diff --git a/tests/cases/fourslash/codeFixAddMissingMember9.ts b/tests/cases/fourslash/codeFixAddMissingMember9.ts
new file mode 100644
index 00000000000..afe401f350b
--- /dev/null
+++ b/tests/cases/fourslash/codeFixAddMissingMember9.ts
@@ -0,0 +1,24 @@
+///
+
+////class C {
+//// z: boolean = true;
+//// method() {
+//// const x = 0;
+//// this.y(x, "a", this.z);
+//// }
+////}
+
+verify.codeFixAll({
+ fixId: "addMissingMember",
+ newFileContent:
+`class C {
+ y(x: number, arg1: string, z: boolean): any {
+ throw new Error("Method not implemented.");
+ }
+ z: boolean = true;
+ method() {
+ const x = 0;
+ this.y(x, "a", this.z);
+ }
+}`,
+});
diff --git a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts
index a946b60f173..5d494e877e3 100644
--- a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts
+++ b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts
@@ -10,6 +10,7 @@
//// let c = new X.C;
//// c.m1();
//// c.y = {};
+//// c.m2(c);
// @Filename: f1.ts
//// export class C {[|
@@ -21,14 +22,18 @@ verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
+verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
verify.rangeIs(`
+ m2(c: C): any {
+ throw new Error("Method not implemented.");
+ }
y: {};
m1(): any {
throw new Error("Method not implemented.");
}
static x: any;
- static m0(arg0: any, arg1: any, arg2: any): any {
+ static m0(arg0: number, arg1: string, arg2: undefined[]): any {
throw new Error("Method not implemented.");
}
`);
\ No newline at end of file
diff --git a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles3.ts b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles3.ts
new file mode 100644
index 00000000000..2b07493f923
--- /dev/null
+++ b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles3.ts
@@ -0,0 +1,26 @@
+///
+
+// @allowJs: true
+// @checkJs: true
+
+// @Filename: f3.ts
+//// import { C } from "./f1";
+//// import { D } from "./f2";
+//// const c = new C();
+//// c.m0(new D());
+
+// @Filename: f2.ts
+//// export class D { }
+
+// @Filename: f1.ts
+//// export class C {[|
+//// |]x: number;
+//// }
+
+verify.getAndApplyCodeFix(/*errorCode*/ undefined, 0);
+
+verify.rangeIs(`
+ m0(arg0: D): any {
+ throw new Error("Method not implemented.");
+ }
+`);
\ No newline at end of file
diff --git a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts
index 0bd4ac3a879..4552f204df1 100644
--- a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts
+++ b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts
@@ -13,7 +13,7 @@ verify.codeFix({
description: "Declare static method 'm1'",
index: 0,
newRangeContent: `
- static m1(arg0: any, arg1: any, arg2: any): any {
+ static m1(arg0: number, arg1: number, arg2: number): any {
throw new Error("Method not implemented.");
}
`,
@@ -23,10 +23,10 @@ verify.codeFix({
description: "Declare static method 'm2'",
index: 0,
newRangeContent: `
- static m2(arg0: any, arg1: any): any {
+ static m2(arg0: number, arg1: number): any {
throw new Error("Method not implemented.");
}
- static m1(arg0: any, arg1: any, arg2: any): any {
+ static m1(arg0: number, arg1: number, arg2: number): any {
throw new Error("Method not implemented.");
}
`,
@@ -37,10 +37,10 @@ verify.codeFix({
index: 0,
newRangeContent: `
static prop1: number;
- static m2(arg0: any, arg1: any): any {
+ static m2(arg0: number, arg1: number): any {
throw new Error("Method not implemented.");
}
- static m1(arg0: any, arg1: any, arg2: any): any {
+ static m1(arg0: number, arg1: number, arg2: number): any {
throw new Error("Method not implemented.");
}
`,
@@ -52,10 +52,10 @@ verify.codeFix({
newRangeContent: `
static prop2: string;
static prop1: number;
- static m2(arg0: any, arg1: any): any {
+ static m2(arg0: number, arg1: number): any {
throw new Error("Method not implemented.");
}
- static m1(arg0: any, arg1: any, arg2: any): any {
+ static m1(arg0: number, arg1: number, arg2: number): any {
throw new Error("Method not implemented.");
}
`,
diff --git a/tests/cases/fourslash/codeFixUndeclaredMethod.ts b/tests/cases/fourslash/codeFixUndeclaredMethod.ts
index 70ed58b995b..795b7052a68 100644
--- a/tests/cases/fourslash/codeFixUndeclaredMethod.ts
+++ b/tests/cases/fourslash/codeFixUndeclaredMethod.ts
@@ -14,7 +14,7 @@ verify.codeFix({
description: "Declare method 'foo1'",
index: 0,
newRangeContent: `
- foo1(arg0: any, arg1: any, arg2: any): any {
+ foo1(arg0: number, arg1: number, arg2: number): any {
throw new Error("Method not implemented.");
}
`,
@@ -27,7 +27,7 @@ verify.codeFix({
foo2(): any {
throw new Error("Method not implemented.");
}
- foo1(arg0: any, arg1: any, arg2: any): any {
+ foo1(arg0: number, arg1: number, arg2: number): any {
throw new Error("Method not implemented.");
}
`
@@ -43,7 +43,7 @@ verify.codeFix({
foo2(): any {
throw new Error("Method not implemented.");
}
- foo1(arg0: any, arg1: any, arg2: any): any {
+ foo1(arg0: number, arg1: number, arg2: number): any {
throw new Error("Method not implemented.");
}
`