Skip to content

Commit

Permalink
fix(prefer-tacit): don't check member functions by default (#808)
Browse files Browse the repository at this point in the history
fix #805
  • Loading branch information
RebeccaStevens authored Apr 15, 2024
1 parent 5d9fc6c commit 4e6c3a9
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 5 deletions.
56 changes: 56 additions & 0 deletions docs/rules/prefer-tacit.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,60 @@ function f(x) {
}

const foo = [1, 2, 3].map(f);

const bar = { f };
const baz = [1, 2, 3].map((x) => bar.f(x)); // Allowed unless using `checkMemberExpressions`
```

## Options

This rule accepts an options object of the following type:

```ts
type Options = {
checkMemberExpressions: boolean;
};
```

### Default Options

```ts
type Options = {
checkMemberExpressions: false;
};
```

### `checkMemberExpressions`

If `true`, calls of member expressions are checked as well.
If `false`, only calls of identifiers are checked.

#### ❌ Incorrect

<!-- eslint-skip -->

```ts
/* eslint functional/prefer-tacit: ["error", { "checkMemberExpressions": true }] */

const bar = {
f(x) {
return x + 1;
}
}

const foo = [1, 2, 3].map((x) => bar.f(x));
```

#### ✅ Correct

```ts
/* eslint functional/prefer-tacit: ["error", { "checkMemberExpressions": true }] */

const bar = {
f(x) {
return x + 1;
}
}

const foo = [1, 2, 3].map(bar.f.bind(bar));
```
42 changes: 37 additions & 5 deletions src/rules/prefer-tacit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
isBlockStatement,
isCallExpression,
isIdentifier,
isMemberExpression,
isReturnStatement,
} from "#eslint-plugin-functional/utils/type-guards";

Expand All @@ -41,17 +42,35 @@ export const fullName = `${ruleNameScope}/${name}`;
/**
* The options this rule can take.
*/
type Options = [];
type Options = [
{
checkMemberExpressions: boolean;
},
];

/**
* The schema for the rule options.
*/
const schema: JSONSchema4[] = [];
const schema: JSONSchema4[] = [
{
type: "object",
properties: {
checkMemberExpressions: {
type: "boolean",
},
},
additionalProperties: false,
},
];

/**
* The default options for the rule.
*/
const defaultOptions: Options = [];
const defaultOptions: Options = [
{
checkMemberExpressions: false,
},
];

/**
* The possible error messages.
Expand Down Expand Up @@ -135,8 +154,12 @@ function fixFunctionCallToReference(

return [
fixer.replaceText(
node as TSESTree.Node,
context.sourceCode.getText(caller.callee as TSESTree.Node),
node,
isMemberExpression(caller.callee)
? `${context.sourceCode.getText(
caller.callee,
)}.bind(${context.sourceCode.getText(caller.callee.object)})`
: context.sourceCode.getText(caller.callee),
),
];
}
Expand Down Expand Up @@ -196,6 +219,15 @@ function getCallDescriptors(
options: Options,
caller: TSESTree.CallExpression,
): Array<ReportDescriptor<keyof typeof errorMessages>> {
const [{ checkMemberExpressions }] = options;

if (
!isIdentifier(caller.callee) &&
!(checkMemberExpressions && isMemberExpression(caller.callee))
) {
return [];
}

if (
node.params.length === caller.arguments.length &&
node.params.every((param, index) => {
Expand Down
47 changes: 47 additions & 0 deletions tests/rules/prefer-tacit/ts/invalid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,53 @@ const tests: Array<
},
],
},
// Member Call Expression
{
code: dedent`
[''].filter(str => /a/.test(str));
`,
optionsSet: [[{ checkMemberExpressions: true }]],
errors: [
{
messageId: "generic",
type: AST_NODE_TYPES.ArrowFunctionExpression,
line: 1,
column: 13,
suggestions: [
{
messageId: "generic",
output: dedent`
[''].filter(/a/.test.bind(/a/));
`,
},
],
},
],
},
{
code: dedent`
declare const a: { b(arg: string): string; };
function foo(x) { return a.b(x); }
`,
optionsSet: [[{ checkMemberExpressions: true }]],
errors: [
{
messageId: "generic",
type: AST_NODE_TYPES.FunctionDeclaration,
line: 2,
column: 1,
suggestions: [
{
messageId: "generic",
output: dedent`
declare const a: { b(arg: string): string; };
const foo = a.b.bind(a);
`,
},
],
},
],
},
];

export default tests;
14 changes: 14 additions & 0 deletions tests/rules/prefer-tacit/ts/valid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ const tests: Array<ValidTestCaseSet<OptionsOf<typeof rule>>> = [
dependencyConstraints: { typescript: "4.7.0" },
optionsSet: [[]],
},
// Member Call Expression
{
code: dedent`
[''].filter(str => /a/.test(str))
`,
optionsSet: [[{ checkMemberExpressions: false }]],
},
{
code: dedent`
declare const a: { b(arg: string): string; };
function foo(x) { return a.b(x); }
`,
optionsSet: [[{ checkMemberExpressions: false }]],
},
];

export default tests;

0 comments on commit 4e6c3a9

Please sign in to comment.