From 9a387b03e5f8b7d2ca9c0e373387c17a1a20b4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20D=C3=A1nyi?= Date: Wed, 19 Jun 2019 19:18:03 +0200 Subject: [PATCH] fix(eslint-plugin): [promise-function-async] allow any as return value (#553) --- .../docs/rules/promise-function-async.md | 2 + .../src/rules/promise-function-async.ts | 10 +++- packages/eslint-plugin/src/util/types.ts | 7 +-- .../rules/promise-function-async.test.ts | 49 ++++++++++++++++++- 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/promise-function-async.md b/packages/eslint-plugin/docs/rules/promise-function-async.md index c64c25c11b5..e28de05742f 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 (`false` 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 885580c493f..4d5c96d76b4 100644 --- a/packages/eslint-plugin/src/rules/promise-function-async.ts +++ b/packages/eslint-plugin/src/rules/promise-function-async.ts @@ -3,6 +3,7 @@ import * as util from '../util'; type Options = [ { + allowAny?: boolean; allowedPromiseNames?: string[]; checkArrowFunctions?: boolean; checkFunctionDeclarations?: boolean; @@ -29,6 +30,9 @@ export default util.createRule({ { type: 'object', properties: { + allowAny: { + type: 'boolean', + }, allowedPromiseNames: { type: 'array', items: { @@ -54,6 +58,7 @@ export default util.createRule({ }, defaultOptions: [ { + allowAny: false, allowedPromiseNames: [], checkArrowFunctions: true, checkFunctionDeclarations: true, @@ -65,6 +70,7 @@ export default util.createRule({ context, [ { + allowAny, allowedPromiseNames, checkArrowFunctions, checkFunctionDeclarations, @@ -90,7 +96,9 @@ export default util.createRule({ } const returnType = checker.getReturnTypeOfSignature(signatures[0]); - if (!util.containsTypeByName(returnType, allAllowedPromiseNames)) { + if ( + !util.containsTypeByName(returnType, allowAny!, allAllowedPromiseNames) + ) { return; } diff --git a/packages/eslint-plugin/src/util/types.ts b/packages/eslint-plugin/src/util/types.ts index 04eb720126a..f10bda7f731 100644 --- a/packages/eslint-plugin/src/util/types.ts +++ b/packages/eslint-plugin/src/util/types.ts @@ -12,10 +12,11 @@ import ts from 'typescript'; */ export function containsTypeByName( type: ts.Type, + allowAny: boolean, allowedNames: Set, ): boolean { if (isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { - return true; + return !allowAny; } if (isTypeReference(type)) { @@ -30,13 +31,13 @@ export function containsTypeByName( } if (isUnionOrIntersectionType(type)) { - return type.types.some(t => containsTypeByName(t, allowedNames)); + return type.types.some(t => containsTypeByName(t, allowAny, allowedNames)); } const bases = type.getBaseTypes(); return ( typeof bases !== 'undefined' && - bases.some(t => containsTypeByName(t, allowedNames)) + bases.some(t => containsTypeByName(t, allowAny, allowedNames)) ); } 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 13b4a18c2e8..1b14ae5d131 100644 --- a/packages/eslint-plugin/tests/rules/promise-function-async.test.ts +++ b/packages/eslint-plugin/tests/rules/promise-function-async.test.ts @@ -30,7 +30,6 @@ const asyncPromiseFunctionExpressionB = async function() { return new Promise n; - public nonAsyncNonPromiseMethod() { return 0; } @@ -71,10 +70,58 @@ const invalidAsyncModifiers = { `export function valid(n: number) { return n; }`, `export default function invalid(n: number) { return n; }`, `class Foo { constructor() { } }`, + { + code: ` +function returnsAny(): any { + return 0; +} + `, + options: [ + { + allowAny: true, + }, + ], + }, + { + code: ` +function returnsUnknown(): unknown { + return 0; +} + `, + options: [ + { + allowAny: true, + }, + ], + }, ], invalid: [ { code: ` +function returnsAny(): any { + return 0; +} + `, + errors: [ + { + messageId, + }, + ], + }, + { + code: ` +function returnsUnknown(): unknown { + return 0; +} + `, + errors: [ + { + messageId, + }, + ], + }, + { + code: ` const nonAsyncPromiseFunctionExpressionA = function(p: Promise) { return p; }; `, errors: [