Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork2.8k
feat(eslint-plugin): added new rule promise-function-async#194
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
JamesHenry merged 13 commits intotypescript-eslint:masterfromJoshuaKGoldberg:typescript-eslint-promise-function-asyncFeb 7, 2019
Uh oh!
There was an error while loading.Please reload this page.
Merged
Changes fromall commits
Commits
Show all changes
13 commits Select commitHold shift + click to select a range
e132cac
feat: (eslint-plugin) added new rule await-promise
34d72fd
Merge branch 'master' into typescript-eslint-promise-function-async
336d231
Merge branch master; moved util to its own file
eed1310
Corrected containsTypeByName to be able to reference itself
64788ca
Merge branch 'master'
0d85213
Fixed more merge conflicts...
c2fe169
Merge branch 'master'
c45499e
Update packages/eslint-plugin/lib/rules/promise-function-async.js
bradzacher1a4d17d
Fixed formatting post-suggestion
a1f4554
Simplified explanations
d362386
Merge branch 'master' into typescript-eslint-promise-function-async
ea0dd69
Merge branch 'master' into typescript-eslint-promise-function-async
JamesHenry131a158
Merge branch 'master' into typescript-eslint-promise-function-async
bradzacherFile filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
1 change: 1 addition & 0 deletionspackages/eslint-plugin/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletionpackages/eslint-plugin/ROADMAP.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletionspackages/eslint-plugin/docs/rules/promise-function-async.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Functions that return promises must be async (promise-function-async) | ||
Requires any function or method that returns a Promise to be marked async. | ||
Ensures that each function is only capable of: | ||
- returning a rejected promise, or | ||
- throwing an Error object. | ||
In contrast, non-`async` `Promise`-returning functions are technically capable of either. | ||
Code that handles the results of those functions will often need to handle both cases, which can get complex. | ||
This rule's practice removes a requirement for creating code to handle both cases. | ||
## Rule Details | ||
Examples of **incorrect** code for this rule | ||
```ts | ||
const arrowFunctionReturnsPromise = () => Promise.resolve('value'); | ||
function functionDeturnsPromise() { | ||
return Math.random() > 0.5 ? Promise.resolve('value') : false; | ||
} | ||
``` | ||
Examples of **correct** code for this rule | ||
```ts | ||
const arrowFunctionReturnsPromise = async () => 'value'; | ||
async function functionDeturnsPromise() { | ||
return Math.random() > 0.5 ? 'value' : false; | ||
} | ||
``` | ||
## Options | ||
Options may be provided as an object with: | ||
- `allowedPromiseNames` to indicate any extra names of classes or interfaces to be considered Promises when returned. | ||
In addition, each of the following properties may be provided, and default to `true`: | ||
- `checkArrowFunctions` | ||
- `checkFunctionDeclarations` | ||
- `checkFunctionExpressions` | ||
- `checkMethodDeclarations` | ||
```json | ||
{ | ||
"@typescript-eslint/promise-function-async": [ | ||
"error", | ||
{ | ||
"allowedPromiseNames": ["Thenable"], | ||
"checkArrowFunctions": true, | ||
"checkFunctionDeclarations": true, | ||
"checkFunctionExpressions": true, | ||
"checkMethodDeclarations": true | ||
} | ||
] | ||
} | ||
``` | ||
## Related To | ||
- TSLint: [promise-function-async](https://palantir.github.io/tslint/rules/promise-function-async) |
125 changes: 125 additions & 0 deletionspackages/eslint-plugin/lib/rules/promise-function-async.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
/** | ||
* @fileoverview Requires any function or method that returns a Promise to be marked async | ||
* @author Josh Goldberg <https://github.com/joshuakgoldberg> | ||
*/ | ||
'use strict'; | ||
const util = require('../util'); | ||
const types = require('../utils/types'); | ||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
//------------------------------------------------------------------------------ | ||
const defaultOptions = [ | ||
{ | ||
allowedPromiseNames: [], | ||
checkArrowFunctions: true, | ||
checkFunctionDeclarations: true, | ||
checkFunctionExpressions: true, | ||
checkMethodDeclarations: true | ||
} | ||
]; | ||
/** | ||
* @type {import("eslint").Rule.RuleModule} | ||
*/ | ||
module.exports = { | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
description: | ||
'Requires any function or method that returns a Promise to be marked async.', | ||
extraDescription: [util.tslintRule('promise-function-async')], | ||
category: 'TypeScript', | ||
url: util.metaDocsUrl('promise-function-async'), | ||
recommended: 'error' | ||
}, | ||
fixable: null, | ||
messages: { | ||
missingAsync: 'Functions that return promises must be async.' | ||
}, | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
allowedPromiseNames: { | ||
type: 'array', | ||
items: { | ||
type: 'string' | ||
} | ||
}, | ||
checkArrowFunctions: { | ||
type: 'boolean' | ||
}, | ||
checkFunctionDeclarations: { | ||
type: 'boolean' | ||
}, | ||
checkFunctionExpressions: { | ||
type: 'boolean' | ||
}, | ||
checkMethodDeclarations: { | ||
type: 'boolean' | ||
} | ||
}, | ||
additionalProperties: false | ||
} | ||
] | ||
}, | ||
create(context) { | ||
const { | ||
allowedPromiseNames, | ||
checkArrowFunctions, | ||
checkFunctionDeclarations, | ||
checkFunctionExpressions, | ||
checkMethodDeclarations | ||
} = util.applyDefault(defaultOptions, context.options)[0]; | ||
const allAllowedPromiseNames = new Set(['Promise', ...allowedPromiseNames]); | ||
const parserServices = util.getParserServices(context); | ||
const checker = parserServices.program.getTypeChecker(); | ||
/** | ||
* @param {import("estree").Function} node | ||
*/ | ||
function validateNode(node) { | ||
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); | ||
const [callSignature] = checker | ||
.getTypeAtLocation(originalNode) | ||
.getCallSignatures(); | ||
const returnType = checker.getReturnTypeOfSignature(callSignature); | ||
if (!types.containsTypeByName(returnType, allAllowedPromiseNames)) { | ||
return; | ||
} | ||
context.report({ | ||
messageId: 'missingAsync', | ||
node | ||
}); | ||
} | ||
return { | ||
ArrowFunctionExpression(node) { | ||
if (checkArrowFunctions && !node.async) { | ||
validateNode(node); | ||
} | ||
}, | ||
FunctionDeclaration(node) { | ||
if (checkFunctionDeclarations && !node.async) { | ||
validateNode(node); | ||
} | ||
}, | ||
FunctionExpression(node) { | ||
if (!!node.parent && node.parent.kind === 'method') { | ||
if (checkMethodDeclarations && !node.async) { | ||
validateNode(node.parent); | ||
} | ||
} else if (checkFunctionExpressions && !node.async) { | ||
validateNode(node); | ||
} | ||
} | ||
}; | ||
} | ||
}; |
38 changes: 38 additions & 0 deletionspackages/eslint-plugin/lib/utils/types.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
'use strict'; | ||
const tsutils = require('tsutils'); | ||
const ts = require('typescript'); | ||
/** | ||
* @param {string} type Type being checked by name. | ||
* @param {Set<string>} allowedNames Symbol names checking on the type. | ||
* @returns {boolean} Whether the type is, extends, or contains any of the allowed names. | ||
*/ | ||
function containsTypeByName(type, allowedNames) { | ||
if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { | ||
return true; | ||
} | ||
if (tsutils.isTypeReference(type)) { | ||
type = type.target; | ||
} | ||
if ( | ||
typeof type.symbol !== 'undefined' && | ||
allowedNames.has(type.symbol.name) | ||
) { | ||
return true; | ||
} | ||
if (tsutils.isUnionOrIntersectionType(type)) { | ||
return type.types.some(t => containsTypeByName(t, allowedNames)); | ||
} | ||
const bases = type.getBaseTypes(); | ||
return ( | ||
typeof bases !== 'undefined' && | ||
bases.some(t => containsTypeByName(t, allowedNames)) | ||
); | ||
} | ||
exports.containsTypeByName = containsTypeByName; |
Oops, something went wrong.
Uh oh!
There was an error while loading.Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.