-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat(eslint-plugin): [await-thenable] check for-await loop iteree #10008
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
Changes from all commits
353bb30
e4ae52b
cb20a7f
0351749
a19dc08
b3aafe6
128c7a1
89a6ab8
de03f50
7cd8ba7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import type { TSESLint } from '@typescript-eslint/utils'; | ||
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; | ||
import * as tsutils from 'ts-api-utils'; | ||
|
||
import { | ||
|
@@ -10,8 +10,15 @@ import { | |
nullThrows, | ||
NullThrowsReasons, | ||
} from '../util'; | ||
import { getForStatementHeadLoc } from '../util/getForStatementHeadLoc'; | ||
|
||
export default createRule({ | ||
type MessageId = | ||
| 'await' | ||
| 'forAwaitOfNonThenable' | ||
| 'removeAwait' | ||
| 'convertToOrdinaryFor'; | ||
|
||
export default createRule<[], MessageId>({ | ||
name: 'await-thenable', | ||
meta: { | ||
docs: { | ||
|
@@ -22,7 +29,10 @@ export default createRule({ | |
hasSuggestions: true, | ||
messages: { | ||
await: 'Unexpected `await` of a non-Promise (non-"Thenable") value.', | ||
forAwaitOfNonThenable: | ||
'Unexpected `for await...of` of a value that is not async iterable.', | ||
removeAwait: 'Remove unnecessary `await`.', | ||
convertToOrdinaryFor: 'Convert to an ordinary `for...of` loop.', | ||
}, | ||
schema: [], | ||
type: 'problem', | ||
|
@@ -62,6 +72,40 @@ export default createRule({ | |
}); | ||
} | ||
}, | ||
|
||
'ForOfStatement[await=true]'(node: TSESTree.ForOfStatement): void { | ||
const type = services.getTypeAtLocation(node.right); | ||
if (isTypeAnyType(type)) { | ||
return; | ||
} | ||
|
||
const asyncIteratorSymbol = tsutils.getWellKnownSymbolPropertyOfType( | ||
type, | ||
'asyncIterator', | ||
checker, | ||
); | ||
|
||
if (asyncIteratorSymbol == null) { | ||
context.report({ | ||
loc: getForStatementHeadLoc(context.sourceCode, node), | ||
messageId: 'forAwaitOfNonThenable', | ||
suggest: [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we only offer this suggestion if the iterator result is not thenable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so because we'd need If we check arrays and tuples specifically, i.e. known iterables, this is silly since for (const promisedValue of await Promise.all(promises)) {
} is a much better suggestion in those cases (since Whereas with lazy sync-iterables of promises, which I don't know if we can detect, it may be better to do for (const promise of yieldPromises()) {
const promisedValue = await promise;
} I guess the only thing that's unambiguous here is that if you have an array or tuple of non-thenables, this suggestion is good. Maybe that's the only situation where we give a suggestion? |
||
// Note that this suggestion causes broken code for sync iterables | ||
// of promises, since the loop variable is not awaited. | ||
{ | ||
messageId: 'convertToOrdinaryFor', | ||
fix(fixer): TSESLint.RuleFix { | ||
const awaitToken = nullThrows( | ||
context.sourceCode.getFirstToken(node, isAwaitKeyword), | ||
NullThrowsReasons.MissingToken('await', 'for await loop'), | ||
); | ||
return fixer.remove(awaitToken); | ||
}, | ||
}, | ||
], | ||
}); | ||
} | ||
}, | ||
}; | ||
}, | ||
}); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.