Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(eslint-plugin): [no-misused-promises] warn when spreading promises#5053

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 56 additions & 3 deletionspackages/eslint-plugin/docs/rules/no-misused-promises.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,9 +13,9 @@ See [`no-floating-promises`](./no-floating-promises.md) for detecting unhandled

## Rule Details

This rule accepts a single option which is an object with `checksConditionals`
and `checksVoidReturn` properties indicating which types of misuse to flag.
Both are enabled by default.
This rule accepts a single option which is an object with `checksConditionals`,
`checksVoidReturn`,and `checksSpreads` properties indicating which types of
misuse to flag. All are enabled by default.

## Options

Expand All@@ -24,6 +24,7 @@ type Options = [
{
checksConditionals?: boolean;
checksVoidReturn?: boolean | ChecksVoidReturnOptions;
checksSpreads?: boolean;
},
];

Expand All@@ -39,6 +40,7 @@ const defaultOptions: Options = [
{
checksConditionals: true,
checksVoidReturn: true,
checksSpreads: true,
},
];
```
Expand DownExpand Up@@ -101,6 +103,21 @@ For example, if you don't mind that passing a `() => Promise<void>` to a `() =>
}
```

### `"checksSpreads"`

If you don't want to check object spreads, you can add this configuration:

```json
{
"@typescript-eslint/no-misused-promises": [
"error",
{
"checksSpreads": false
}
]
}
```

### `checksConditionals: true`

Examples of code for this rule with `checksConditionals: true`:
Expand DownExpand Up@@ -212,6 +229,42 @@ eventEmitter.on('some-event', () => {

<!--/tabs-->

### `checksSpreads: true`

Examples of code for this rule with `checksSpreads: true`:

<!--tabs-->

#### ❌ Incorrect

```ts
const getData = () => someAsyncOperation({ myArg: 'foo' });

return { foo: 42, ...getData() };

const getData2 = async () => {
await someAsyncOperation({ myArg: 'foo' });
};

return { foo: 42, ...getData2() };
```

#### ✅ Correct

```ts
const getData = () => someAsyncOperation({ myArg: 'foo' });

return { foo: 42, ...(await getData()) };

const getData2 = async () => {
await someAsyncOperation({ myArg: 'foo' });
};

return { foo: 42, ...(await getData2()) };
```

<!--tabs-->

## When Not To Use It

If you do not use Promises in your codebase or are not concerned with possible
Expand Down
39 changes: 37 additions & 2 deletionspackages/eslint-plugin/src/rules/no-misused-promises.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ type Options = [
{
checksConditionals?: boolean;
checksVoidReturn?: boolean | ChecksVoidReturnOptions;
checksSpreads?: boolean;
},
];

Expand All@@ -25,7 +26,8 @@ type MessageId =
| 'voidReturnVariable'
| 'voidReturnProperty'
| 'voidReturnReturnValue'
| 'voidReturnAttribute';
| 'voidReturnAttribute'
| 'spread';

function parseChecksVoidReturn(
checksVoidReturn: boolean | ChecksVoidReturnOptions | undefined,
Expand DownExpand Up@@ -75,6 +77,7 @@ export default util.createRule<Options, MessageId>({
voidReturnAttribute:
'Promise-returning function provided to attribute where a void return was expected.',
conditional: 'Expected non-Promise value in a boolean conditional.',
spread: 'Expected a non-Promise value to be spreaded in an object.',
},
schema: [
{
Expand All@@ -99,6 +102,9 @@ export default util.createRule<Options, MessageId>({
},
],
},
checksSpreads: {
type: 'boolean',
},
},
},
],
Expand All@@ -108,10 +114,11 @@ export default util.createRule<Options, MessageId>({
{
checksConditionals: true,
checksVoidReturn: true,
checksSpreads: true,
},
],

create(context, [{ checksConditionals, checksVoidReturn }]) {
create(context, [{ checksConditionals, checksVoidReturn, checksSpreads }]) {
const parserServices = util.getParserServices(context);
const checker = parserServices.program.getTypeChecker();

Expand DownExpand Up@@ -153,6 +160,10 @@ export default util.createRule<Options, MessageId>({
}
: {};

const spreadChecks: TSESLint.RuleListener = {
SpreadElement: checkSpread,
};

function checkTestConditional(node: {
test: TSESTree.Expression | null;
}): void {
Expand DownExpand Up@@ -376,13 +387,37 @@ export default util.createRule<Options, MessageId>({
}
}

function checkSpread(node: TSESTree.SpreadElement): void {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);

if (isSometimesThenable(checker, tsNode.expression)) {
context.report({
messageId: 'spread',
node: node.argument,
});
}
}

return {
...(checksConditionals ? conditionalChecks : {}),
...(checksVoidReturn ? voidReturnChecks : {}),
...(checksSpreads ? spreadChecks : {}),
};
},
});

function isSometimesThenable(checker: ts.TypeChecker, node: ts.Node): boolean {
const type = checker.getTypeAtLocation(node);

for (const subType of tsutils.unionTypeParts(checker.getApparentType(type))) {
if (tsutils.isThenableType(checker, node, subType)) {
return true;
}
}

return false;
}

// Variation on the thenable check which requires all forms of the type (read:
// alternates in a union) to be thenable. Otherwise, you might be trying to
// check if something is defined or undefined and get caught because one of the
Expand Down
100 changes: 100 additions & 0 deletionspackages/eslint-plugin/tests/rules/no-misused-promises.test.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -316,6 +316,63 @@ const _ = <Component onEvent={async () => {}} />;
`,
filename: 'react.tsx',
},
`
console.log({ ...(await Promise.resolve({ key: 42 })) });
`,
`
const getData = Promise.resolve({ key: 42 });

console.log({
someData: 42,
...(await getData()),
});
`,
`
declare const condition: boolean;

console.log({ ...(condition && (await Promise.resolve({ key: 42 }))) });
console.log({ ...(condition || (await Promise.resolve({ key: 42 }))) });
console.log({ ...(condition ? {} : await Promise.resolve({ key: 42 })) });
console.log({ ...(condition ? await Promise.resolve({ key: 42 }) : {}) });
`,
`
console.log([...(await Promise.resolve(42))]);
`,
{
code: `
console.log({ ...Promise.resolve({ key: 42 }) });
`,
options: [{ checksSpreads: false }],
},
{
code: `
const getData = Promise.resolve({ key: 42 });

console.log({
someData: 42,
...getData(),
});
`,
options: [{ checksSpreads: false }],
},
{
code: `
declare const condition: boolean;

console.log({ ...(condition && Promise.resolve({ key: 42 })) });
console.log({ ...(condition || Promise.resolve({ key: 42 })) });
console.log({ ...(condition ? {} : Promise.resolve({ key: 42 })) });
console.log({ ...(condition ? Promise.resolve({ key: 42 }) : {}) });
`,
options: [{ checksSpreads: false }],
},
{
code: `
// This is invalid Typescript, but it shouldn't trigger this linter specifically
console.log([...Promise.resolve(42)]);
`,
options: [{ checksSpreads: false }],
},
],

invalid: [
Expand DownExpand Up@@ -870,5 +927,48 @@ it('', async () => {});
},
],
},
{
code: `
console.log({ ...Promise.resolve({ key: 42 }) });
`,
errors: [
{
line: 2,
messageId: 'spread',
},
],
},
{
code: `
const getData = () => Promise.resolve({ key: 42 });

console.log({
someData: 42,
...getData(),
});
`,
errors: [
{
line: 6,
messageId: 'spread',
},
],
},
{
code: `
declare const condition: boolean;

console.log({ ...(condition && Promise.resolve({ key: 42 })) });
console.log({ ...(condition || Promise.resolve({ key: 42 })) });
console.log({ ...(condition ? {} : Promise.resolve({ key: 42 })) });
console.log({ ...(condition ? Promise.resolve({ key: 42 }) : {}) });
`,
errors: [
{ line: 4, messageId: 'spread' },
{ line: 5, messageId: 'spread' },
{ line: 6, messageId: 'spread' },
{ line: 7, messageId: 'spread' },
],
},
],
});

[8]ページ先頭

©2009-2025 Movatter.jp