Skip to content

Codefix: add quick fix for missing 'new' operator #27019

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

9 changes: 8 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4732,7 +4732,6 @@
"category": "Message",
"code": 95062
},

"Add missing enum member '{0}'": {
"category": "Message",
"code": 95063
Expand Down Expand Up @@ -4764,5 +4763,13 @@
"Add 'unknown' to all conversions of non-overlapping types": {
"category": "Message",
"code": 95070
},
"Add missing 'new' operator to call": {
"category": "Message",
"code": 95071
},
"Add missing 'new' operator to all calls": {
"category": "Message",
"code": 95072
}
}
32 changes: 32 additions & 0 deletions src/services/codefixes/fixAddMissingNewOperator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* @internal */
namespace ts.codefix {
const fixId = "addMissingNewOperator";
const errorCodes = [Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new.code];
registerCodeFix({
errorCodes,
getCodeActions(context) {
const { sourceFile, span } = context;
const changes = textChanges.ChangeTracker.with(context, t => addMissingNewOperator(t, sourceFile, span));
return [createCodeFixAction(fixId, changes, Diagnostics.Add_missing_new_operator_to_call, fixId, Diagnostics.Add_missing_new_operator_to_all_calls)];
},
fixIds: [fixId],
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) =>
addMissingNewOperator(changes, context.sourceFile, diag)),
});

function addMissingNewOperator(changes: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan): void {
const call = cast(findAncestorMatchingSpan(sourceFile, span), isCallExpression);
const newExpression = createNew(call.expression, call.typeArguments, call.arguments);

changes.replaceNode(sourceFile, call, newExpression);
}

function findAncestorMatchingSpan(sourceFile: SourceFile, span: TextSpan): Node {
let token = getTokenAtPosition(sourceFile, span.start);
const end = textSpanEnd(span);
while (token.end < end) {
token = token.parent;
}
return token;
}
}
1 change: 1 addition & 0 deletions src/services/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"codefixes/importFixes.ts",
"codefixes/fixSpelling.ts",
"codefixes/fixAddMissingMember.ts",
"codefixes/fixAddMissingNewOperator.ts",
"codefixes/fixCannotFindModule.ts",
"codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
"codefixes/fixClassSuperMustPrecedeThisAccess.ts",
Expand Down
14 changes: 14 additions & 0 deletions tests/cases/fourslash/codeFixAddMissingNew.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />

////class C {
////}
////var c = C();

verify.codeFix({
description: "Add missing 'new' operator to call",
index: 0,
newFileContent:
`class C {
}
var c = new C();`
});
14 changes: 14 additions & 0 deletions tests/cases/fourslash/codeFixAddMissingNew2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />

////class C {
////}
////let x = (() => C)()();

verify.codeFix({
description: "Add missing 'new' operator to call",
index: 0,
newFileContent:
`class C {
}
let x = new ((() => C)())();`
});
16 changes: 16 additions & 0 deletions tests/cases/fourslash/codeFixAddMissingNew3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />

////class C {
////}
////let x = [C];
////let a = x[0]();

verify.codeFix({
description: "Add missing 'new' operator to call",
index: 0,
newFileContent:
`class C {
}
let x = [C];
let a = new x[0]();`
});
18 changes: 18 additions & 0 deletions tests/cases/fourslash/codeFixAddMissingNew4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />

////class C {
////}
////class D {
////}
////let x = (true ? C : D)();

verify.codeFix({
description: "Add missing 'new' operator to call",
index: 0,
newFileContent:
`class C {
}
class D {
}
let x = new (true ? C : D)();`
});
25 changes: 25 additions & 0 deletions tests/cases/fourslash/codeFixAddMissingNew5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/// <reference path='fourslash.ts' />

////class C {
////}
////
////function foo() {
//// return C;
////}
////
////foo()!();


verify.codeFix({
description: "Add missing 'new' operator to call",
index: 0,
newFileContent:
`class C {
}

function foo() {
return C;
}

new (foo()!)();`
});
18 changes: 18 additions & 0 deletions tests/cases/fourslash/codeFixAddMissingNew_all.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />

////class C {
//// constructor(num?: number) {}
////}
////var a = C();
////var b = C(3);

verify.codeFixAll({
fixId: "addMissingNewOperator",
fixAllDescription: "Add missing 'new' operator to all calls",
newFileContent:
`class C {
constructor(num?: number) {}
}
var a = new C();
var b = new C(3);`
});
22 changes: 22 additions & 0 deletions tests/cases/fourslash/codeFixAddMissingNew_all_arguments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// <reference path='fourslash.ts' />

////class C<T = number> {
//// x?: T;
//// constructor(x: T) { this.x = x; }
////}
////let a = C(1, 2, 3);
////let b = C<string>("hello");
////let c = C<boolean>();

verify.codeFixAll({
fixId: "addMissingNewOperator",
fixAllDescription: "Add missing 'new' operator to all calls",
newFileContent:
`class C<T = number> {
x?: T;
constructor(x: T) { this.x = x; }
}
let a = new C(1, 2, 3);
let b = new C<string>("hello");
let c = new C<boolean>();`
});