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)",