mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-11 10:46:28 -05:00
Infer parameter names and types when applying Delcare Method codefix (#22180)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
24
tests/cases/fourslash/codeFixAddMissingMember9.ts
Normal file
24
tests/cases/fourslash/codeFixAddMissingMember9.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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);
|
||||
}
|
||||
}`,
|
||||
});
|
||||
@@ -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.");
|
||||
}
|
||||
`);
|
||||
26
tests/cases/fourslash/codeFixUndeclaredAcrossFiles3.ts
Normal file
26
tests/cases/fourslash/codeFixUndeclaredAcrossFiles3.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @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.");
|
||||
}
|
||||
`);
|
||||
@@ -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.");
|
||||
}
|
||||
`,
|
||||
|
||||
@@ -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<T, U, V, W, X, Y, Z>(): 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<T, U, V, W, X, Y, Z>(): 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.");
|
||||
}
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user