diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 9a918507dc4..5bcaf448599 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -4811,5 +4811,9 @@
"Add names to all parameters without names": {
"category": "Message",
"code": 95073
+ },
+ "Enable the 'experimentalDecorators' option in your configuration file": {
+ "category": "Message",
+ "code": 95074
}
}
diff --git a/src/services/codefixes/fixCannotFindModule.ts b/src/services/codefixes/fixCannotFindModule.ts
index 1c7070c74bf..1baaa9714f2 100644
--- a/src/services/codefixes/fixCannotFindModule.ts
+++ b/src/services/codefixes/fixCannotFindModule.ts
@@ -74,7 +74,7 @@ namespace ts.codefix {
const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile);
if (!tsconfigObjectLiteral) return undefined;
- const compilerOptionsProperty = findProperty(tsconfigObjectLiteral, "compilerOptions");
+ const compilerOptionsProperty = findJsonProperty(tsconfigObjectLiteral, "compilerOptions");
if (!compilerOptionsProperty) {
const newCompilerOptions = createObjectLiteral([makeDefaultBaseUrl(), makeDefaultPaths()]);
changes.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createJsonPropertyAssignment("compilerOptions", newCompilerOptions));
@@ -94,7 +94,7 @@ namespace ts.codefix {
return createJsonPropertyAssignment("baseUrl", createStringLiteral(defaultBaseUrl));
}
function getOrAddBaseUrl(changes: textChanges.ChangeTracker, tsconfig: TsConfigSourceFile, compilerOptions: ObjectLiteralExpression): string {
- const baseUrlProp = findProperty(compilerOptions, "baseUrl");
+ const baseUrlProp = findJsonProperty(compilerOptions, "baseUrl");
if (baseUrlProp) {
return isStringLiteral(baseUrlProp.initializer) ? baseUrlProp.initializer.text : defaultBaseUrl;
}
@@ -112,7 +112,7 @@ namespace ts.codefix {
return createJsonPropertyAssignment("paths", createObjectLiteral([makeDefaultPathMapping()]));
}
function getOrAddPathMapping(changes: textChanges.ChangeTracker, tsconfig: TsConfigSourceFile, compilerOptions: ObjectLiteralExpression) {
- const paths = findProperty(compilerOptions, "paths");
+ const paths = findJsonProperty(compilerOptions, "paths");
if (!paths || !isObjectLiteralExpression(paths.initializer)) {
changes.insertNodeAtObjectStart(tsconfig, compilerOptions, makeDefaultPaths());
return defaultTypesDirectoryName;
@@ -129,14 +129,6 @@ namespace ts.codefix {
return defaultTypesDirectoryName;
}
- function createJsonPropertyAssignment(name: string, initializer: Expression) {
- return createPropertyAssignment(createStringLiteral(name), initializer);
- }
-
- function findProperty(obj: ObjectLiteralExpression, name: string): PropertyAssignment | undefined {
- return find(obj.properties, (p): p is PropertyAssignment => isPropertyAssignment(p) && !!p.name && isStringLiteral(p.name) && p.name.text === name);
- }
-
function getInstallCommand(fileName: string, packageName: string): InstallPackageAction {
return { type: "install package", file: fileName, packageName };
}
diff --git a/src/services/codefixes/fixEnableExperimentalDecorators.ts b/src/services/codefixes/fixEnableExperimentalDecorators.ts
new file mode 100644
index 00000000000..8179abdfd1d
--- /dev/null
+++ b/src/services/codefixes/fixEnableExperimentalDecorators.ts
@@ -0,0 +1,60 @@
+/* @internal */
+namespace ts.codefix {
+ const fixId = "enableExperimentalDecorators";
+ const errorCodes = [
+ Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_to_remove_this_warning.code
+ ];
+ registerCodeFix({
+ errorCodes,
+ getCodeActions: (context) => {
+ const { configFile } = context.program.getCompilerOptions();
+ if (configFile === undefined) {
+ return undefined;
+ }
+
+ const changes = textChanges.ChangeTracker.with(context, changeTracker => makeChange(changeTracker, configFile));
+ return [createCodeFixActionNoFixId(fixId, changes, Diagnostics.Enable_the_experimentalDecorators_option_in_your_configuration_file)];
+ },
+ fixIds: [fixId],
+ });
+
+ function makeChange(changeTracker: textChanges.ChangeTracker, configFile: TsConfigSourceFile) {
+ const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile);
+ if (tsconfigObjectLiteral === undefined) {
+ return;
+ }
+
+ const compilerOptionsProperty = findJsonProperty(tsconfigObjectLiteral, "compilerOptions");
+ if (compilerOptionsProperty === undefined) {
+ changeTracker.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createCompilerOptionsAssignment());
+ return;
+ }
+
+ const compilerOptions = compilerOptionsProperty.initializer;
+ if (!isObjectLiteralExpression(compilerOptions)) {
+ return;
+ }
+
+ const experimentalDecoratorsProperty = findJsonProperty(compilerOptions, "experimentalDecorators");
+
+ if (experimentalDecoratorsProperty === undefined) {
+ changeTracker.insertNodeAtObjectStart(configFile, compilerOptions, createExperimentalDecoratorsAssignment());
+ }
+ else {
+ changeTracker.replaceNodeWithText(configFile, experimentalDecoratorsProperty.initializer, "true");
+ }
+ }
+
+ function createCompilerOptionsAssignment() {
+ return createJsonPropertyAssignment(
+ "compilerOptions",
+ createObjectLiteral([
+ createExperimentalDecoratorsAssignment(),
+ ]),
+ );
+ }
+
+ function createExperimentalDecoratorsAssignment() {
+ return createJsonPropertyAssignment("experimentalDecorators", createTrue());
+ }
+}
diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts
index 461771c0b5e..0344f51a854 100644
--- a/src/services/codefixes/helpers.ts
+++ b/src/services/codefixes/helpers.ts
@@ -1,5 +1,13 @@
/* @internal */
namespace ts.codefix {
+ export function createJsonPropertyAssignment(name: string, initializer: Expression) {
+ return createPropertyAssignment(createStringLiteral(name), initializer);
+ }
+
+ export function findJsonProperty(obj: ObjectLiteralExpression, name: string): PropertyAssignment | undefined {
+ return find(obj.properties, (p): p is PropertyAssignment => isPropertyAssignment(p) && !!p.name && isStringLiteral(p.name) && p.name.text === name);
+ }
+
/**
* Finds members of the resolved type that are missing in the class pointed to by class decl
* and generates source code for the missing members.
diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json
index 15044416f84..21be663055a 100644
--- a/src/services/tsconfig.json
+++ b/src/services/tsconfig.json
@@ -61,6 +61,7 @@
"codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
"codefixes/fixClassSuperMustPrecedeThisAccess.ts",
"codefixes/fixConstructorForDerivedNeedSuperCall.ts",
+ "codefixes/fixEnableExperimentalDecorators.ts",
"codefixes/fixExtendsInterfaceBecomesImplements.ts",
"codefixes/fixForgottenThisPropertyAccess.ts",
"codefixes/fixUnusedIdentifier.ts",
diff --git a/tests/cases/fourslash/codefixEnableExperimentalDecorators_blankCompilerOptions.ts b/tests/cases/fourslash/codefixEnableExperimentalDecorators_blankCompilerOptions.ts
new file mode 100644
index 00000000000..0e19b42d921
--- /dev/null
+++ b/tests/cases/fourslash/codefixEnableExperimentalDecorators_blankCompilerOptions.ts
@@ -0,0 +1,26 @@
+///
+
+// @Filename: /dir/a.ts
+////declare const decorator: any;
+////class A {
+//// @decorator method() {};
+////};
+
+// @Filename: /dir/tsconfig.json
+////{
+//// "compilerOptions": {
+//// }
+////}
+
+goTo.file("/dir/a.ts");
+verify.codeFix({
+ description: "Enable the 'experimentalDecorators' option in your configuration file",
+ newFileContent: {
+ "/dir/tsconfig.json":
+`{
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ }
+}`,
+ },
+});
diff --git a/tests/cases/fourslash/codefixEnableExperimentalDecorators_disabledInCompilerOptions.ts b/tests/cases/fourslash/codefixEnableExperimentalDecorators_disabledInCompilerOptions.ts
new file mode 100644
index 00000000000..056e7e15a48
--- /dev/null
+++ b/tests/cases/fourslash/codefixEnableExperimentalDecorators_disabledInCompilerOptions.ts
@@ -0,0 +1,27 @@
+///
+
+// @Filename: /dir/a.ts
+////declare const decorator: any;
+////class A {
+//// @decorator method() {};
+////};
+
+// @Filename: /dir/tsconfig.json
+////{
+//// "compilerOptions": {
+//// "experimentalDecorators": false,
+//// }
+////}
+
+goTo.file("/dir/a.ts");
+verify.codeFix({
+ description: "Enable the 'experimentalDecorators' option in your configuration file",
+ newFileContent: {
+ "/dir/tsconfig.json":
+`{
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ }
+}`,
+ },
+});
diff --git a/tests/cases/fourslash/codefixEnableExperimentalDecorators_missingCompilerOptions.ts b/tests/cases/fourslash/codefixEnableExperimentalDecorators_missingCompilerOptions.ts
new file mode 100644
index 00000000000..6d7006f3ce1
--- /dev/null
+++ b/tests/cases/fourslash/codefixEnableExperimentalDecorators_missingCompilerOptions.ts
@@ -0,0 +1,22 @@
+///
+
+// @Filename: /dir/a.ts
+////declare const decorator: any;
+////class A {
+//// @decorator method() {};
+////};
+
+// @Filename: /dir/tsconfig.json
+////{
+////}
+
+goTo.file("/dir/a.ts");
+verify.codeFix({
+ description: "Enable the 'experimentalDecorators' option in your configuration file",
+ newFileContent: {
+ "/dir/tsconfig.json":
+`{
+ "compilerOptions": { "experimentalDecorators": true },
+}`,
+ },
+});
diff --git a/tests/cases/fourslash/codefixEnableExperimentalDecorators_noTsconfig.ts b/tests/cases/fourslash/codefixEnableExperimentalDecorators_noTsconfig.ts
new file mode 100644
index 00000000000..8430fd0734b
--- /dev/null
+++ b/tests/cases/fourslash/codefixEnableExperimentalDecorators_noTsconfig.ts
@@ -0,0 +1,10 @@
+///
+
+// @Filename: /dir/a.ts
+////declare const decorator: any;
+////class A {
+//// @decorator method() {};
+////};
+
+goTo.file("/dir/a.ts");
+verify.not.codeFixAvailable();