From ceb01e092d75aa98f4c4ea576ae92ffa10c50f88 Mon Sep 17 00:00:00 2001 From: lonyele Date: Fri, 23 Aug 2019 15:50:35 +0900 Subject: [PATCH 1/4] feat: add allowAny options to restric-plus-operands rule --- .../src/rules/restrict-plus-operands.ts | 34 +++++++++++--- .../rules/restrict-plus-operands.test.ts | 47 +++++++++++++++++++ 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts index 1f715f1f902..8ebb2e36691 100644 --- a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts +++ b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts @@ -2,7 +2,15 @@ import { TSESTree } from '@typescript-eslint/experimental-utils'; import ts from 'typescript'; import * as util from '../util'; -export default util.createRule({ +type Options = [ + { + allowAny?: boolean; + }, +]; + +type MessageId = 'notStrings' | 'notBigInts' | 'notNumbers'; + +export default util.createRule({ name: 'restrict-plus-operands', meta: { type: 'problem', @@ -20,15 +28,24 @@ export default util.createRule({ "Operands of '+' operation must either be both strings or both numbers. Consider using a template literal.", notBigInts: "Operands of '+' operation must be both bigints.", }, - schema: [], + schema: [ + { + type: 'object', + properties: { + allowAny: { + type: 'boolean', + }, + }, + }, + ], }, - defaultOptions: [], - create(context) { + defaultOptions: [{ allowAny: false }], + create(context, [option]) { const service = util.getParserServices(context); const typeChecker = service.program.getTypeChecker(); - type BaseLiteral = 'string' | 'number' | 'bigint' | 'invalid'; + type BaseLiteral = 'string' | 'number' | 'bigint' | 'invalid' | 'any'; /** * Helper function to get base type of node @@ -65,7 +82,8 @@ export default util.createRule({ if ( stringType === 'number' || stringType === 'string' || - stringType === 'bigint' + stringType === 'bigint' || + stringType === 'any' ) { return stringType; } @@ -88,6 +106,10 @@ export default util.createRule({ const leftType = getNodeType(node.left); const rightType = getNodeType(node.right); + if (option.allowAny && (leftType === 'any' || rightType === 'any')) { + return; + } + if ( leftType === 'invalid' || rightType === 'invalid' || diff --git a/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts b/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts index 44583a202a1..c124d2aa940 100644 --- a/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts @@ -81,6 +81,36 @@ function foo(a: T) { return a + 1; } `, + { + code: ` +export const f = (a: any, b: any) => a + b; + `, + options: [ + { + allowAny: true, + }, + ], + }, + { + code: ` +export const f = (a: string, b: any) => a + b; + `, + options: [ + { + allowAny: true, + }, + ], + }, + { + code: ` +export const f = (a: boolean, b: any) => a + b; + `, + options: [ + { + allowAny: true, + }, + ], + }, ], invalid: [ { @@ -383,5 +413,22 @@ function foo(a: T) { }, ], }, + { + code: ` +export const f = (a: string, b: number) => a + b; + `, + errors: [ + { + messageId: 'notStrings', + line: 2, + column: 44, + }, + ], + options: [ + { + allowAny: true, + }, + ], + }, ], }); From 40e64194d3ffe8123883920181ae6c54d1afb803 Mon Sep 17 00:00:00 2001 From: lonyele Date: Sat, 24 Aug 2019 09:21:55 +0900 Subject: [PATCH 2/4] docs: add about allowAny option --- .../docs/rules/restrict-plus-operands.md | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/restrict-plus-operands.md b/packages/eslint-plugin/docs/rules/restrict-plus-operands.md index 1efffa83e69..77353b74ece 100644 --- a/packages/eslint-plugin/docs/rules/restrict-plus-operands.md +++ b/packages/eslint-plugin/docs/rules/restrict-plus-operands.md @@ -16,12 +16,34 @@ var foo = 1n + 1; ## Options +Options may be provided as an object with: + +- `allowAny` to ignore this rule when either left or + right of plus operand is a type `any` + ```json { - "@typescript-eslint/restrict-plus-operands": "error" + "@typescript-eslint/estrict-plus-operands": [ + "error", + { + "allowAny": true + } + ] } ``` +### allowAny + +If the rule is too strict then making this option `true` +can be a help. Though It is not recommended since lint errors are potentially a real runtime errors in many cases. + +Examples of **correct** code for this rule with `{ allowAny: true`: + +```ts +const x = (a: any, b: string) => a + b; +const y = (a: boolean, b: any) => a + b; +``` + ## Compatibility - TSLint: [restrict-plus-operands](https://palantir.github.io/tslint/rules/restrict-plus-operands/) From 795317fa4d6c3d32d6c25984df5693164479bb20 Mon Sep 17 00:00:00 2001 From: lonyele Date: Mon, 6 Dec 2021 18:52:13 +0900 Subject: [PATCH 3/4] feat: add allowAny option to the rule --- .../src/rules/restrict-plus-operands.ts | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts index 30c155e3dcd..afcbf9abf2f 100644 --- a/packages/eslint-plugin/src/rules/restrict-plus-operands.ts +++ b/packages/eslint-plugin/src/rules/restrict-plus-operands.ts @@ -5,6 +5,7 @@ import * as util from '../util'; type Options = [ { checkCompoundAssignments?: boolean; + allowAny?: boolean; }, ]; type MessageIds = 'notNumbers' | 'notStrings' | 'notBigInts'; @@ -34,6 +35,9 @@ export default util.createRule({ checkCompoundAssignments: { type: 'boolean', }, + allowAny: { + type: 'boolean', + }, }, }, ], @@ -41,13 +45,14 @@ export default util.createRule({ defaultOptions: [ { checkCompoundAssignments: false, + allowAny: false, }, ], - create(context, [{ checkCompoundAssignments }]) { + create(context, [{ checkCompoundAssignments, allowAny }]) { const service = util.getParserServices(context); const typeChecker = service.program.getTypeChecker(); - type BaseLiteral = 'string' | 'number' | 'bigint' | 'invalid'; + type BaseLiteral = 'string' | 'number' | 'bigint' | 'invalid' | 'any'; /** * Helper function to get base type of node @@ -82,7 +87,8 @@ export default util.createRule({ if ( stringType === 'number' || stringType === 'string' || - stringType === 'bigint' + stringType === 'bigint' || + stringType === 'any' ) { return stringType; } @@ -108,28 +114,53 @@ export default util.createRule({ const leftType = getNodeType(node.left); const rightType = getNodeType(node.right); - if ( - leftType === 'invalid' || - rightType === 'invalid' || - leftType !== rightType - ) { - if (leftType === 'string' || rightType === 'string') { + if (leftType === rightType) { + if (leftType === 'invalid') { context.report({ node, - messageId: 'notStrings', + messageId: 'notNumbers', }); - } else if (leftType === 'bigint' || rightType === 'bigint') { + } + + if (!allowAny && leftType === 'any') { context.report({ node, - messageId: 'notBigInts', + messageId: 'notNumbers', }); - } else { + } + + return; + } + + if (leftType === 'any' || rightType === 'any') { + if (!allowAny || leftType === 'invalid' || rightType === 'invalid') { context.report({ node, messageId: 'notNumbers', }); } + + return; } + + if (leftType === 'string' || rightType === 'string') { + return context.report({ + node, + messageId: 'notStrings', + }); + } + + if (leftType === 'bigint' || rightType === 'bigint') { + return context.report({ + node, + messageId: 'notBigInts', + }); + } + + context.report({ + node, + messageId: 'notNumbers', + }); } return { From 5a9dab3e9dcf7d17d186f19c0ae86c96ecfb5188 Mon Sep 17 00:00:00 2001 From: lonyele Date: Mon, 6 Dec 2021 18:52:37 +0900 Subject: [PATCH 4/4] test: add more cases for validate change --- .../rules/restrict-plus-operands.test.ts | 162 +++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts b/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts index af33e3ad3db..12b7e44657e 100644 --- a/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts +++ b/packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts @@ -139,6 +139,46 @@ foo += 'string'; }, ], }, + { + code: ` +export const f = (a: any, b: any) => a + b; + `, + options: [ + { + allowAny: true, + }, + ], + }, + { + code: ` +export const f = (a: any, b: string) => a + b; + `, + options: [ + { + allowAny: true, + }, + ], + }, + { + code: ` +export const f = (a: any, b: bigint) => a + b; + `, + options: [ + { + allowAny: true, + }, + ], + }, + { + code: ` +export const f = (a: any, b: number) => a + b; + `, + options: [ + { + allowAny: true, + }, + ], + }, ], invalid: [ { @@ -515,7 +555,7 @@ function foo(a: T) { `, errors: [ { - messageId: 'notStrings', + messageId: 'notNumbers', line: 4, column: 19, }, @@ -571,5 +611,125 @@ foo += 0; }, ], }, + { + code: ` +const f = (a: any, b: boolean) => a + b; + `, + options: [ + { + allowAny: true, + }, + ], + errors: [ + { + messageId: 'notNumbers', + line: 2, + column: 35, + }, + ], + }, + { + code: ` +const f = (a: any, b: []) => a + b; + `, + options: [ + { + allowAny: true, + }, + ], + errors: [ + { + messageId: 'notNumbers', + line: 2, + column: 30, + }, + ], + }, + + { + code: ` +const f = (a: any, b: any) => a + b; + `, + options: [ + { + allowAny: false, + }, + ], + errors: [ + { + messageId: 'notNumbers', + line: 2, + column: 31, + }, + ], + }, + { + code: ` +const f = (a: any, b: string) => a + b; + `, + options: [ + { + allowAny: false, + }, + ], + errors: [ + { + messageId: 'notNumbers', + line: 2, + column: 34, + }, + ], + }, + { + code: ` +const f = (a: any, b: bigint) => a + b; + `, + options: [ + { + allowAny: false, + }, + ], + errors: [ + { + messageId: 'notNumbers', + line: 2, + column: 34, + }, + ], + }, + { + code: ` +const f = (a: any, b: number) => a + b; + `, + options: [ + { + allowAny: false, + }, + ], + errors: [ + { + messageId: 'notNumbers', + line: 2, + column: 34, + }, + ], + }, + { + code: ` +const f = (a: any, b: boolean) => a + b; + `, + options: [ + { + allowAny: false, + }, + ], + errors: [ + { + messageId: 'notNumbers', + line: 2, + column: 35, + }, + ], + }, ], });