From f06ecdf78b6d6f366434d73a6acfe7041d575223 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Fri, 9 Apr 2021 17:53:22 +0200 Subject: [PATCH] Update: Add disallowTemplateShorthand option in no-implicit-coercion (#13579) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update: Add templateString option in no-implicit-coercion (fixes #12866) This adds the `templateString` option to `no-implicit-coercion`. This makes the rule report the following code: ```js `${foo}` ``` For backwards compatibility, this was added as a separate option instead of as default behaviour. * Fix no-implicit-coercion templateString examples * Skip tagged template strings in no-implicit-coercion * Add missing tests for no-implicit-coercion * Used cooked template string values * Add missing documentation for no-implicit-coercion Note that the `templateString` option isn’t affected by the `string` option. * Add missing tests for no-implicit-coercion templateString * Rename templateString to disallowTemplateString * Fix typo in no-implicit-coercion docs * Add tagged template string example for no-implicit-coercion * Update docs/rules/no-implicit-coercion.md Co-authored-by: Milos Djermanovic Co-authored-by: Milos Djermanovic --- docs/rules/no-implicit-coercion.md | 45 ++++++++++++++++++++++--- lib/rules/no-implicit-coercion.js | 37 ++++++++++++++++++++ tests/lib/rules/no-implicit-coercion.js | 40 ++++++++++++++++++++++ 3 files changed, 118 insertions(+), 4 deletions(-) diff --git a/docs/rules/no-implicit-coercion.md b/docs/rules/no-implicit-coercion.md index f78e40c66c2..8ccee339f05 100644 --- a/docs/rules/no-implicit-coercion.md +++ b/docs/rules/no-implicit-coercion.md @@ -33,10 +33,11 @@ This rule is aimed to flag shorter notations for the type conversion, then sugge This rule has three main options and one override option to allow some coercions as required. -* `"boolean"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `boolean` type. -* `"number"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `number` type. -* `"string"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `string` type. -* `"allow"` (`empty` by default) - Each entry in this array can be one of `~`, `!!`, `+` or `*` that are to be allowed. +- `"boolean"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `boolean` type. +- `"number"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `number` type. +- `"string"` (`true` by default) - When this is `true`, this rule warns shorter type conversions for `string` type. +- `"disallowTemplateShorthand"` (`false` by default) - When this is `true`, this rule warns `string` type conversions using `${expression}` form. +- `"allow"` (`empty` by default) - Each entry in this array can be one of `~`, `!!`, `+` or `*` that are to be allowed. Note that operator `+` in `allow` list would allow `+foo` (number coercion) as well as `"" + foo` (string coercion). @@ -106,6 +107,42 @@ var s = String(foo); foo = String(foo); ``` +### disallowTemplateShorthand + +This option is **not** affected by the `string` option. + +Examples of **incorrect** code for the `{ "disallowTemplateShorthand": true }` option: + +```js +/*eslint no-implicit-coercion: ["error", { "disallowTemplateShorthand": true }]*/ + +var s = `${foo}`; +``` + +Examples of **correct** code for the `{ "disallowTemplateShorthand": true }` option: + +```js +/*eslint no-implicit-coercion: ["error", { "disallowTemplateShorthand": true }]*/ + +var s = String(foo); + +var s = `a${foo}`; + +var s = `${foo}b`; + +var s = `${foo}${bar}`; + +var s = tag`${foo}`; +``` + +Examples of **correct** code for the default `{ "disallowTemplateShorthand": false }` option: + +```js +/*eslint no-implicit-coercion: ["error", { "disallowTemplateShorthand": false }]*/ + +var s = `${foo}`; +``` + ### allow Using `allow` list, we can override and allow specific operators. diff --git a/lib/rules/no-implicit-coercion.js b/lib/rules/no-implicit-coercion.js index a639711ecea..b9bb4548986 100644 --- a/lib/rules/no-implicit-coercion.js +++ b/lib/rules/no-implicit-coercion.js @@ -24,6 +24,7 @@ function parseOptions(options) { boolean: "boolean" in options ? options.boolean : true, number: "number" in options ? options.number : true, string: "string" in options ? options.string : true, + disallowTemplateShorthand: "disallowTemplateShorthand" in options ? options.disallowTemplateShorthand : false, allow: options.allow || [] }; } @@ -180,6 +181,10 @@ module.exports = { type: "boolean", default: true }, + disallowTemplateShorthand: { + type: "boolean", + default: false + }, allow: { type: "array", items: { @@ -299,6 +304,38 @@ module.exports = { report(node, recommendation, true); } + }, + + TemplateLiteral(node) { + if (!options.disallowTemplateShorthand) { + return; + } + + // tag`${foo}` + if (node.parent.type === "TaggedTemplateExpression") { + return; + } + + // `` or `${foo}${bar}` + if (node.expressions.length !== 1) { + return; + } + + + // `prefix${foo}` + if (node.quasis[0].value.cooked !== "") { + return; + } + + // `${foo}postfix` + if (node.quasis[1].value.cooked !== "") { + return; + } + + const code = sourceCode.getText(node.expressions[0]); + const recommendation = `String(${code})`; + + report(node, recommendation, true); } }; } diff --git a/tests/lib/rules/no-implicit-coercion.js b/tests/lib/rules/no-implicit-coercion.js index fa2b68b4975..fa0a63824bb 100644 --- a/tests/lib/rules/no-implicit-coercion.js +++ b/tests/lib/rules/no-implicit-coercion.js @@ -88,6 +88,13 @@ ruleTester.run("no-implicit-coercion", rule, { { code: "`${foo}` + ''", parserOptions: { ecmaVersion: 6 } }, "foo += 'bar'", { code: "foo += `${bar}`", parserOptions: { ecmaVersion: 6 } }, + { code: "`a${foo}`", options: [{ disallowTemplateShorthand: true }], parserOptions: { ecmaVersion: 6 } }, + { code: "`${foo}b`", options: [{ disallowTemplateShorthand: true }], parserOptions: { ecmaVersion: 6 } }, + { code: "`${foo}${bar}`", options: [{ disallowTemplateShorthand: true }], parserOptions: { ecmaVersion: 6 } }, + { code: "tag`${foo}`", options: [{ disallowTemplateShorthand: true }], parserOptions: { ecmaVersion: 6 } }, + { code: "`${foo}`", parserOptions: { ecmaVersion: 6 } }, + { code: "`${foo}`", options: [{ }], parserOptions: { ecmaVersion: 6 } }, + { code: "`${foo}`", options: [{ disallowTemplateShorthand: false }], parserOptions: { ecmaVersion: 6 } }, "+42" ], invalid: [ @@ -248,6 +255,39 @@ ruleTester.run("no-implicit-coercion", rule, { type: "BinaryExpression" }] }, + { + code: "`${foo}`", + output: "String(foo)", + options: [{ disallowTemplateShorthand: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "useRecommendation", + data: { recommendation: "String(foo)" }, + type: "TemplateLiteral" + }] + }, + { + code: "`\\\n${foo}`", + output: "String(foo)", + options: [{ disallowTemplateShorthand: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "useRecommendation", + data: { recommendation: "String(foo)" }, + type: "TemplateLiteral" + }] + }, + { + code: "`${foo}\\\n`", + output: "String(foo)", + options: [{ disallowTemplateShorthand: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "useRecommendation", + data: { recommendation: "String(foo)" }, + type: "TemplateLiteral" + }] + }, { code: "foo += \"\"", output: "foo = String(foo)",