diff --git a/packages/eslint-plugin/docs/rules/promise-function-async.md b/packages/eslint-plugin/docs/rules/promise-function-async.md index c64c25c11b5a..a0b702d6bd68 100644 --- a/packages/eslint-plugin/docs/rules/promise-function-async.md +++ b/packages/eslint-plugin/docs/rules/promise-function-async.md @@ -36,6 +36,7 @@ async function functionDeturnsPromise() { Options may be provided as an object with: +- `allowAny` to indicate that `any` or `unknown` shouldn't be considered Promises (`true` by default). - `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`: @@ -50,6 +51,7 @@ In addition, each of the following properties may be provided, and default to `t "@typescript-eslint/promise-function-async": [ "error", { + "allowAny": true, "allowedPromiseNames": ["Thenable"], "checkArrowFunctions": true, "checkFunctionDeclarations": true, diff --git a/packages/eslint-plugin/src/rules/promise-function-async.ts b/packages/eslint-plugin/src/rules/promise-function-async.ts index 885580c493fa..44214d44dd5c 100644 --- a/packages/eslint-plugin/src/rules/promise-function-async.ts +++ b/packages/eslint-plugin/src/rules/promise-function-async.ts @@ -1,8 +1,11 @@ +import ts from 'typescript'; +import { isTypeFlagSet } from 'tsutils'; import { TSESTree } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; type Options = [ { + allowAny?: boolean; allowedPromiseNames?: string[]; checkArrowFunctions?: boolean; checkFunctionDeclarations?: boolean; @@ -29,6 +32,9 @@ export default util.createRule({ { type: 'object', properties: { + allowAny: { + type: 'boolean', + }, allowedPromiseNames: { type: 'array', items: { @@ -54,6 +60,7 @@ export default util.createRule({ }, defaultOptions: [ { + allowAny: true, allowedPromiseNames: [], checkArrowFunctions: true, checkFunctionDeclarations: true, @@ -65,6 +72,7 @@ export default util.createRule({ context, [ { + allowAny, allowedPromiseNames, checkArrowFunctions, checkFunctionDeclarations, @@ -90,6 +98,12 @@ export default util.createRule({ } const returnType = checker.getReturnTypeOfSignature(signatures[0]); + if ( + allowAny && + isTypeFlagSet(returnType, ts.TypeFlags.Any | ts.TypeFlags.Unknown) + ) { + return; + } if (!util.containsTypeByName(returnType, allAllowedPromiseNames)) { return; } diff --git a/packages/eslint-plugin/tests/rules/promise-function-async.test.ts b/packages/eslint-plugin/tests/rules/promise-function-async.test.ts index 3910cea56042..d3b84bbdd313 100644 --- a/packages/eslint-plugin/tests/rules/promise-function-async.test.ts +++ b/packages/eslint-plugin/tests/rules/promise-function-async.test.ts @@ -16,6 +16,16 @@ const ruleTester = new RuleTester({ ruleTester.run('promise-function-async', rule, { valid: [ ` +function returnsAny(): any { + return 0; +} + `, + ` +function returnsUnknown(): unknown { + return 0; +} + `, + ` const nonAsyncNonPromiseArrowFunction = (n: number) => n; `, ` @@ -31,6 +41,10 @@ const asyncPromiseFunctionExpressionB = async function() { return new Promise n; + public constructor() { + return 0 as any; + } + public nonAsyncNonPromiseMethod() { return 0; } @@ -42,6 +56,14 @@ class Test { public async asyncPromiseMethodB() { return new Promise(); } + + public get getterThatReturnsAny() { + return 0 as any; + } + + public set setterThatReturnsAny(value: number) { + return 0 as any; + } } `, // https://github.com/typescript-eslint/typescript-eslint/issues/227 @@ -287,5 +309,41 @@ const returnAllowedType = () => new PromiseType(); }, ], }, + { + code: ` +function returnsAny(): any { + return 0; +} +`, + options: [ + { + allowAny: false, + }, + ], + errors: [ + { + line: 2, + messageId, + }, + ], + }, + { + code: ` +function returnsUnknown(): unknown { + return 0; +} +`, + options: [ + { + allowAny: false, + }, + ], + errors: [ + { + line: 2, + messageId, + }, + ], + }, ], });