From 4c3e704b97e698df7f72174c2d20714836d4d243 Mon Sep 17 00:00:00 2001 From: auvred <61150013+auvred@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:22:38 +0300 Subject: [PATCH] feat(eslint-plugin): [require-await] allow yielding Promise in async generators (#8003) * feat(eslint-plugin): [require-await] allow yielding Promise in async generators * chore: refactor `visitYieldExpression` a bit --- .../eslint-plugin/src/rules/require-await.ts | 21 +++++++++++----- .../tests/rules/require-await.test.ts | 24 +++++++++++++++++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin/src/rules/require-await.ts b/packages/eslint-plugin/src/rules/require-await.ts index f834e17dece..dd033c7cbe2 100644 --- a/packages/eslint-plugin/src/rules/require-await.ts +++ b/packages/eslint-plugin/src/rules/require-await.ts @@ -112,18 +112,27 @@ export default createRule({ } /** - * mark `scopeInfo.isAsyncYield` to `true` if its a generator - * function and the delegate is `true` + * Mark `scopeInfo.isAsyncYield` to `true` if it + * 1) delegates async generator function + * or + * 2) yields thenable type */ - function markAsHasDelegateGen(node: TSESTree.YieldExpression): void { + function visitYieldExpression(node: TSESTree.YieldExpression): void { if (!scopeInfo?.isGen || !node.argument) { return; } if (node.argument.type === AST_NODE_TYPES.Literal) { - // making this `false` as for literals we don't need to check the definition + // ignoring this as for literals we don't need to check the definition // eg : async function* run() { yield* 1 } - scopeInfo.isAsyncYield ||= false; + return; + } + + if (!node.delegate) { + if (isThenableType(services.esTreeNodeToTSNodeMap.get(node.argument))) { + scopeInfo.isAsyncYield = true; + } + return; } const type = services.getTypeAtLocation(node.argument); @@ -152,7 +161,7 @@ export default createRule({ AwaitExpression: markAsHasAwait, 'VariableDeclaration[kind = "await using"]': markAsHasAwait, 'ForOfStatement[await = true]': markAsHasAwait, - 'YieldExpression[delegate = true]': markAsHasDelegateGen, + YieldExpression: visitYieldExpression, // check body-less async arrow function. // ignore `async () => await foo` because it's obviously correct diff --git a/packages/eslint-plugin/tests/rules/require-await.test.ts b/packages/eslint-plugin/tests/rules/require-await.test.ts index 7bcb1d6a0b4..f8f9624e26c 100644 --- a/packages/eslint-plugin/tests/rules/require-await.test.ts +++ b/packages/eslint-plugin/tests/rules/require-await.test.ts @@ -237,6 +237,30 @@ async function* foo(): Promise { await using foo = new Bar(); }; `, + ` + async function* test1() { + yield Promise.resolve(1); + } + `, + ` + function asyncFunction() { + return Promise.resolve(1); + } + async function* test1() { + yield asyncFunction(); + } + `, + ` + declare const asyncFunction: () => Promise; + async function* test1() { + yield asyncFunction(); + } + `, + ` + async function* test1() { + yield new Promise(() => {}); + } + `, ], invalid: [