-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix(eslint-plugin): [require-await] handle async generators #1782
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
Conversation
Thanks for the PR, @anikethsaha! typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community. The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately. Thanks again! 🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. As a thank you, your profile/company logo will be added to our main README which receives thousands of unique visitors per day. |
Codecov Report
@@ Coverage Diff @@
## master #1782 +/- ##
==========================================
- Coverage 94.96% 94.92% -0.05%
==========================================
Files 159 159
Lines 7114 7126 +12
Branches 2035 2042 +7
==========================================
+ Hits 6756 6764 +8
- Misses 155 156 +1
- Partials 203 206 +3
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR
Could you please add some negative tests as well?
To ensure that code like the following aren't considered valid:
async function* foo() {} // no await or async yield
async function* bar() {
return 1; // not a promise
}
async function* baz() {
yield 1; // not an async yield
}
I.e. async generator is valid for this rule if it contains an await
, returns a promise, or contains a yield*
.
These will be valid as these contains generators (along with async). Having generator will be a valid test irrespective of the function body Let me know if I am wrong ! |
Sorry, what I'm saying is that they should not be valid. Adding |
Actually in eslint repo, there is a similar issue in which it states that it should not check for yield as well cause async generators cant be yield that are coming generator function. IMO, I would suggest having a separate rule like Let me know, if you want me to change this, I will add a check for |
Note that you should always take the discussions in the ESLint repo with a pinch of salt in the context of this project, because we run in a typechecked world here. Looking at it from this point of view, which is the question the rule is attempting to ask in a codebase: Does the async function require an await expression?
The last case is why they don't want to check them in the base rule - they cannot detect this case. OTOH, we can check it in here, because we have type information. So rather than just ignoring all async generators, we should just ignore async generators that |
I completely forgot the power of typescript for this 😅
also for returning promises as well right ? |
Yes, sorry to clarify I meant this: |
ok cool, I will make the necessary changes |
@bradzacher can you give an example of these two cases |
// OKAY - async generator has an await
async function* asyncGenerator() {
await Promise.resolve()
yield 1
}
// OKAY - no async
function* syncGenerator() {
yield 1
}
// OKAY - async generator deferring to an async generator
async function* test1() {
yield* asyncGenerator()
}
// TS error - non-async generator deferring to an async generator
function* test2() {
yield* asyncGenerator()
}
// LINT ERROR - async generator has no await and is not deferring to an async generator
async function* test3() {
yield asyncGenerator()
}
// OKAY - no async
function* test4() {
yield asyncGenerator()
}
// LINT ERROR - async generator has no await and is deferring to a non-async generator
async function* test5() {
yield* syncGenerator()
}
// OKAY - no async
function* test6() {
yield* syncGenerator()
}
// LINT ERROR - async generator has no await and is not deferring to an async generator
async function* test7() {
yield syncGenerator()
}
// OKAY - no async
function* test8() {
yield syncGenerator()
} |
Cool, I am adding these example as tests 👍 |
function* test2() {
yield* asyncGenerator()
}
async function* test5() {
yield* syncGenerator()
} or is |
The former is ignored by the rule, yes, as it's not async. The latter should be a lint error:
|
is |
whats the difference between async function* test1() {
yield* asyncGenerator()
} and async function* test1() {
yield* syncGenerator()
} |
Ok got it, here is the defination,
So, we need to check and test the function's declaration as well right to achieve this right ? |
cc @bradzacher |
Yup! Similar to what we currently do for return statements. With a A good place to play with this is ts-ast-viewer If you select one of the call expressions inside the
|
I am not able to get this thing with this site. is it possible if you can show it using Also, this what the latest change looks like
Now , async function* asyncGenerator() {
await Promise.resolve()
yield 1
}
async function* test1() {
yield* asyncGenerator()
} We need to get the definition of |
@bradzacher you can check the diff here with reference to this PR's previous commit |
'YieldExpression[delegate=true]'(node: TSESTree.YieldExpression): void {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node.argument);
const type = checker.getTypeAtLocation(tsNode);
const symbol = type.getSymbol(); // <--- undefined
if (symbol.getName() === 'AsyncGenerator' && scopeInfo) {
scopeInfo.hasAsyncDelegateYield = true;
}
}, @bradzacher |
This is expected - some types won't have an associated symbol. if (symbol?.getName() === 'AsyncGenerator' && scopeInfo) { |
@bradzacher any suggestion for type checks errors !? |
Fixes #1691
!node.generator
before checking starts as to ignore the checks for generators