From 21a26ca08a9bce10845e7f6c700557a61ce69412 Mon Sep 17 00:00:00 2001 From: Alexander T Date: Sun, 28 Jun 2020 11:36:59 +0300 Subject: [PATCH] feat(25259): add better error to report for equals instead of the colon in object literals --- src/compiler/checker.ts | 7 ++-- src/compiler/diagnosticMessages.json | 6 +++- .../codefixes/fixPropertyAssignment.ts | 28 +++++++++++++++ src/services/tsconfig.json | 1 + ...pertyAssignmentsInDestructuring.errors.txt | 9 ++--- ...ndPropertyAssignmentsInDestructuring.types | 4 +-- ...yAssignmentsInDestructuring_ES6.errors.txt | 9 ++--- ...opertyAssignmentsInDestructuring_ES6.types | 4 +-- .../fourslash/codeFixPropertyAssignment1.ts | 14 ++++++++ .../fourslash/codeFixPropertyAssignment2.ts | 14 ++++++++ .../fourslash/codeFixPropertyAssignment3.ts | 18 ++++++++++ .../codeFixPropertyAssignment_fixAll.ts | 34 +++++++++++++++++++ 12 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 src/services/codefixes/fixPropertyAssignment.ts create mode 100644 tests/cases/fourslash/codeFixPropertyAssignment1.ts create mode 100644 tests/cases/fourslash/codeFixPropertyAssignment2.ts create mode 100644 tests/cases/fourslash/codeFixPropertyAssignment3.ts create mode 100644 tests/cases/fourslash/codeFixPropertyAssignment_fixAll.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index aa0091f99523a..6a667916b38d2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23889,7 +23889,10 @@ namespace ts { memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment || isObjectLiteralMethod(memberDecl)) { let type = memberDecl.kind === SyntaxKind.PropertyAssignment ? checkPropertyAssignment(memberDecl, checkMode) : - memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ? checkExpressionForMutableLocation(memberDecl.name, checkMode) : + // avoid resolving the left side of the ShorthandPropertyAssignment outside of the destructuring + // for error recovery purposes. For example, if a user wrote `{ a = 100 }` instead of `{ a: 100 }`. + // we don't want to say "could not find 'a'". + memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ? checkExpressionForMutableLocation(!inDestructuringPattern && memberDecl.objectAssignmentInitializer ? memberDecl.objectAssignmentInitializer : memberDecl.name, checkMode) : checkObjectLiteralMethod(memberDecl, checkMode); if (isInJavascript) { const jsDocType = getTypeForDeclarationFromJSDocComment(memberDecl); @@ -38218,7 +38221,7 @@ namespace ts { if (prop.kind === SyntaxKind.ShorthandPropertyAssignment && !inDestructuring && prop.objectAssignmentInitializer) { // having objectAssignmentInitializer is only valid in ObjectAssignmentPattern // outside of destructuring it is a syntax error - return grammarErrorOnNode(prop.equalsToken!, Diagnostics.can_only_be_used_in_an_object_literal_property_inside_a_destructuring_assignment); + return grammarErrorOnNode(prop.equalsToken!, Diagnostics.Did_you_mean_to_use_a_Colon_When_following_property_names_in_an_object_literal_implies_a_destructuring_assignment); } if (name.kind === SyntaxKind.PrivateIdentifier) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b32e8d020290b..dc21a3581324a 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -880,7 +880,7 @@ "category": "Error", "code": 1308 }, - "'=' can only be used in an object literal property inside a destructuring assignment.": { + "Did you mean to use a ':'? When following property names in an object literal, '=' implies a destructuring assignment.": { "category": "Error", "code": 1312 }, @@ -5819,6 +5819,10 @@ "category": "Message", "code": 95137 }, + "Switch each misused '{0}' to '{1}'": { + "category": "Message", + "code": 95138 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/services/codefixes/fixPropertyAssignment.ts b/src/services/codefixes/fixPropertyAssignment.ts new file mode 100644 index 0000000000000..ee46d90858b83 --- /dev/null +++ b/src/services/codefixes/fixPropertyAssignment.ts @@ -0,0 +1,28 @@ +/* @internal */ +namespace ts.codefix { + const fixId = "fixPropertyAssignment"; + const errorCodes = [ + Diagnostics.Did_you_mean_to_use_a_Colon_When_following_property_names_in_an_object_literal_implies_a_destructuring_assignment.code + ]; + + registerCodeFix({ + errorCodes, + fixIds: [fixId], + getCodeActions(context) { + const { sourceFile, span } = context; + const property = getProperty(sourceFile, span.start); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, property)); + return [createCodeFixAction(fixId, changes, [Diagnostics.Change_0_to_1, "=", ":"], fixId, [Diagnostics.Switch_each_misused_0_to_1, "=", ":"])]; + }, + getAllCodeActions: context => + codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, getProperty(diag.file, diag.start))) + }); + + function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: ShorthandPropertyAssignment): void { + changes.replaceNode(sourceFile, node, factory.createPropertyAssignment(node.name, node.objectAssignmentInitializer as Expression)); + } + + function getProperty(sourceFile: SourceFile, pos: number): ShorthandPropertyAssignment { + return cast(getTokenAtPosition(sourceFile, pos).parent, isShorthandPropertyAssignment); + } +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 9567185c7a9ac..e1aab52f187f1 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -76,6 +76,7 @@ "codefixes/fixEnableExperimentalDecorators.ts", "codefixes/fixEnableJsxFlag.ts", "codefixes/fixModuleAndTargetOptions.ts", + "codefixes/fixPropertyAssignment.ts", "codefixes/fixExtendsInterfaceBecomesImplements.ts", "codefixes/fixForgottenThisPropertyAccess.ts", "codefixes/fixInvalidJsxCharacters.ts", diff --git a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.errors.txt b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.errors.txt index cb22c03a20b60..ba8af08a9cbf3 100644 --- a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.errors.txt +++ b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.errors.txt @@ -11,11 +11,10 @@ tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(85,19): erro Types of property 'x' are incompatible. Type 'number' is not assignable to type 'string'. tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(85,26): error TS2322: Type 'number' is not assignable to type 'string'. -tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(111,12): error TS18004: No value exists in scope for the shorthand property 's'. Either declare one or provide an initializer. -tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(111,14): error TS1312: '=' can only be used in an object literal property inside a destructuring assignment. +tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(111,14): error TS1312: Did you mean to use a ':'? When following property names in an object literal, '=' implies a destructuring assignment. -==== tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts (13 errors) ==== +==== tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts (12 errors) ==== (function() { var s0; for ({ s0 = 5 } of [{ s0: 1 }]) { @@ -153,10 +152,8 @@ tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring.ts(111,14): err (function() { let a = { s = 5 }; - ~ -!!! error TS18004: No value exists in scope for the shorthand property 's'. Either declare one or provide an initializer. ~ -!!! error TS1312: '=' can only be used in an object literal property inside a destructuring assignment. +!!! error TS1312: Did you mean to use a ':'? When following property names in an object literal, '=' implies a destructuring assignment. }); function foo({a = 4, b = { x: 5 }}) { diff --git a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.types b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.types index b160cd31b1290..9e44644904c41 100644 --- a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.types +++ b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.types @@ -417,8 +417,8 @@ >function() { let a = { s = 5 };} : () => void let a = { s = 5 }; ->a : { s: any; } ->{ s = 5 } : { s: any; } +>a : { s: number; } +>{ s = 5 } : { s: number; } >s : any >5 : 5 diff --git a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.errors.txt b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.errors.txt index fb368df29c380..d6b0da61711cb 100644 --- a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.errors.txt +++ b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.errors.txt @@ -11,11 +11,10 @@ tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(85,19): Types of property 'x' are incompatible. Type 'number' is not assignable to type 'string'. tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(85,26): error TS2322: Type 'number' is not assignable to type 'string'. -tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(111,12): error TS18004: No value exists in scope for the shorthand property 's'. Either declare one or provide an initializer. -tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(111,14): error TS1312: '=' can only be used in an object literal property inside a destructuring assignment. +tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(111,14): error TS1312: Did you mean to use a ':'? When following property names in an object literal, '=' implies a destructuring assignment. -==== tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts (13 errors) ==== +==== tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts (12 errors) ==== (function() { var s0; for ({ s0 = 5 } of [{ s0: 1 }]) { @@ -153,10 +152,8 @@ tests/cases/compiler/shorthandPropertyAssignmentsInDestructuring_ES6.ts(111,14): (function() { let a = { s = 5 }; - ~ -!!! error TS18004: No value exists in scope for the shorthand property 's'. Either declare one or provide an initializer. ~ -!!! error TS1312: '=' can only be used in an object literal property inside a destructuring assignment. +!!! error TS1312: Did you mean to use a ':'? When following property names in an object literal, '=' implies a destructuring assignment. }); function foo({a = 4, b = { x: 5 }}) { diff --git a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.types b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.types index 1cdc84e83ba71..68ff6a0a26a7f 100644 --- a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.types +++ b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring_ES6.types @@ -417,8 +417,8 @@ >function() { let a = { s = 5 };} : () => void let a = { s = 5 }; ->a : { s: any; } ->{ s = 5 } : { s: any; } +>a : { s: number; } +>{ s = 5 } : { s: number; } >s : any >5 : 5 diff --git a/tests/cases/fourslash/codeFixPropertyAssignment1.ts b/tests/cases/fourslash/codeFixPropertyAssignment1.ts new file mode 100644 index 0000000000000..3745a91f7540a --- /dev/null +++ b/tests/cases/fourslash/codeFixPropertyAssignment1.ts @@ -0,0 +1,14 @@ +/// + +////const a = { +//// x/**/= 1 +////} + +verify.codeFix({ + description: [ts.Diagnostics.Change_0_to_1.message, "=", ":"], + index: 0, + newFileContent: +`const a = { + x: 1 +}`, +}); diff --git a/tests/cases/fourslash/codeFixPropertyAssignment2.ts b/tests/cases/fourslash/codeFixPropertyAssignment2.ts new file mode 100644 index 0000000000000..233e612a9a054 --- /dev/null +++ b/tests/cases/fourslash/codeFixPropertyAssignment2.ts @@ -0,0 +1,14 @@ +/// + +////const a = { +//// x /**/= 1 +////} + +verify.codeFix({ + description: [ts.Diagnostics.Change_0_to_1.message, "=", ":"], + index: 0, + newFileContent: +`const a = { + x: 1 +}`, +}); diff --git a/tests/cases/fourslash/codeFixPropertyAssignment3.ts b/tests/cases/fourslash/codeFixPropertyAssignment3.ts new file mode 100644 index 0000000000000..8346230588e22 --- /dev/null +++ b/tests/cases/fourslash/codeFixPropertyAssignment3.ts @@ -0,0 +1,18 @@ +/// + +////const a = { +//// x: 1, +//// y /**/= 1, +//// z: 1 +////} + +verify.codeFix({ + description: [ts.Diagnostics.Change_0_to_1.message, "=", ":"], + index: 0, + newFileContent: +`const a = { + x: 1, + y: 1, + z: 1 +}`, +}); diff --git a/tests/cases/fourslash/codeFixPropertyAssignment_fixAll.ts b/tests/cases/fourslash/codeFixPropertyAssignment_fixAll.ts new file mode 100644 index 0000000000000..5cf54db26b1be --- /dev/null +++ b/tests/cases/fourslash/codeFixPropertyAssignment_fixAll.ts @@ -0,0 +1,34 @@ +/// + +////const a = { +//// x: 1, +//// y = 1, +//// z: 1 +////} +////const b = { +//// x = 1, +//// y: 1 +////} +////const c = { +//// x: 1, +//// y = 1 +////} + +verify.codeFixAll({ + fixAllDescription: "Switch each misused '=' to ':'", + fixId: "fixPropertyAssignment", + newFileContent: +`const a = { + x: 1, + y: 1, + z: 1 +} +const b = { + x: 1, + y: 1 +} +const c = { + x: 1, + y: 1 +}` +});