Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): [restrict-plus-operand] add allowAny option #4260

Merged
42 changes: 38 additions & 4 deletions packages/eslint-plugin/docs/rules/restrict-plus-operands.md
Expand Up @@ -22,14 +22,25 @@ var foo = 1n + 1n;

## Options

This rule has an object option:
The rule accepts an options object with the following properties:

- `"checkCompoundAssignments": false`: (default) does not check compound assignments (`+=`)
- `"checkCompoundAssignments": true`
```ts
type Options = {
// if true, check compound assignments (`+=`)
checkCompoundAssignments?: boolean;
// if true, 'any' itself and `string`,`bigint`, `number` is allowed.
allowAny?: boolean;
};

const defaults = {
checkCompoundAssignments: false,
allowAny: false,
};
```

### `checkCompoundAssignments`

Examples of code for the `{ "checkCompoundAssignments": true }` option:
Examples of code for this rule with `{ checkCompoundAssignments: true }`:

<!--tabs-->

Expand Down Expand Up @@ -57,6 +68,29 @@ let bar = '';
bar += 'test';
```

### `allowAny`

Examples of code for this rule with `{ allowAny: true }`:

<!--tabs-->

#### ❌ Incorrect

```ts
var fn = (a: any, b: boolean) => a + b;
var fn = (a: any, b: []) => a + b;
var fn = (a: any, b: {}) => a + b;
```

#### ✅ Correct

```ts
var fn = (a: any, b: any) => a + b;
var fn = (a: any, b: string) => a + b;
var fn = (a: any, b: bigint) => a + b;
var fn = (a: any, b: number) => a + b;
```

## How to Use

```json
Expand Down
57 changes: 44 additions & 13 deletions packages/eslint-plugin/src/rules/restrict-plus-operands.ts
Expand Up @@ -5,6 +5,7 @@ import * as util from '../util';
type Options = [
{
checkCompoundAssignments?: boolean;
allowAny?: boolean;
},
];
type MessageIds = 'notNumbers' | 'notStrings' | 'notBigInts';
lonyele marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -34,20 +35,24 @@ export default util.createRule<Options, MessageIds>({
checkCompoundAssignments: {
type: 'boolean',
},
allowAny: {
type: 'boolean',
},
},
},
],
},
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
Expand Down Expand Up @@ -82,7 +87,8 @@ export default util.createRule<Options, MessageIds>({
if (
stringType === 'number' ||
stringType === 'string' ||
stringType === 'bigint'
stringType === 'bigint' ||
stringType === 'any'
) {
return stringType;
}
Expand All @@ -108,28 +114,53 @@ export default util.createRule<Options, MessageIds>({
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 {
Expand Down
162 changes: 161 additions & 1 deletion packages/eslint-plugin/tests/rules/restrict-plus-operands.test.ts
Expand Up @@ -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: [
{
Expand Down Expand Up @@ -515,7 +555,7 @@ function foo<T extends 1>(a: T) {
`,
errors: [
{
messageId: 'notStrings',
messageId: 'notNumbers',
line: 4,
column: 19,
},
Expand Down Expand Up @@ -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,
},
],
},
],
});