diff --git a/packages/eslint-plugin/docs/rules/promise-function-async.md b/packages/eslint-plugin/docs/rules/promise-function-async.md index 4ca59ac341e..f3095ed1808 100644 --- a/packages/eslint-plugin/docs/rules/promise-function-async.md +++ b/packages/eslint-plugin/docs/rules/promise-function-async.md @@ -46,6 +46,7 @@ async function functionReturnsPromise() { return Promise.resolve('value'); } +// An explicit return type that is not Promise means this function cannot be made async, so it is ignored by the rule function functionReturnsUnionWithPromiseExplicitly( p: boolean, ): string | Promise { diff --git a/packages/eslint-plugin/src/rules/promise-function-async.ts b/packages/eslint-plugin/src/rules/promise-function-async.ts index e8b9f44739b..34fbc7ee672 100644 --- a/packages/eslint-plugin/src/rules/promise-function-async.ts +++ b/packages/eslint-plugin/src/rules/promise-function-async.ts @@ -111,12 +111,12 @@ export default util.createRule({ const returnType = checker.getReturnTypeOfSignature(signatures[0]); if ( - !util.containsTypesByName( + !util.containsAllTypesByName( returnType, allowAny!, allAllowedPromiseNames, - // Only if a return type is explicitly declared must it match all - Boolean(node.returnType), + // If no return type is explicitly set, we check if any parts of the return type match a Promise (instead of requiring all to match). + node.returnType == null, ) ) { // Return type is not a promise diff --git a/packages/type-utils/src/containsTypesByName.ts b/packages/type-utils/src/containsAllTypesByName.ts similarity index 69% rename from packages/type-utils/src/containsTypesByName.ts rename to packages/type-utils/src/containsAllTypesByName.ts index 2e57984ac28..9c3fb522236 100644 --- a/packages/type-utils/src/containsTypesByName.ts +++ b/packages/type-utils/src/containsAllTypesByName.ts @@ -7,14 +7,14 @@ import { isTypeFlagSet } from './typeFlagUtils'; * @param type Type being checked by name. * @param allowAny Whether to consider `any` and `unknown` to match. * @param allowedNames Symbol names checking on the type. - * @param mustMatchAll Whether all parts have to match, as opposed to any parts matching. + * @param matchAnyInstead Whether to instead just check if any parts match, rather than all parts. * @returns Whether the type is, extends, or contains the allowed names (or all matches the allowed names, if mustMatchAll is true). */ -export function containsTypesByName( +export function containsAllTypesByName( type: ts.Type, allowAny: boolean, allowedNames: Set, - mustMatchAll: boolean, + matchAnyInstead = false, ): boolean { if (isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { return !allowAny; @@ -30,20 +30,20 @@ export function containsTypesByName( } const predicate = (t: ts.Type): boolean => - containsTypesByName(t, allowAny, allowedNames, mustMatchAll); + containsAllTypesByName(t, allowAny, allowedNames, matchAnyInstead); if (isUnionOrIntersectionType(type)) { - return mustMatchAll - ? type.types.every(predicate) - : type.types.some(predicate); + return matchAnyInstead + ? type.types.some(predicate) + : type.types.every(predicate); } const bases = type.getBaseTypes(); return ( typeof bases !== 'undefined' && - (mustMatchAll - ? bases.length > 0 && bases.every(predicate) - : bases.some(predicate)) + (matchAnyInstead + ? bases.some(predicate) + : bases.length > 0 && bases.every(predicate)) ); } diff --git a/packages/type-utils/src/index.ts b/packages/type-utils/src/index.ts index a350d5c8658..dde032e1770 100644 --- a/packages/type-utils/src/index.ts +++ b/packages/type-utils/src/index.ts @@ -1,4 +1,4 @@ -export * from './containsTypesByName'; +export * from './containsAllTypesByName'; export * from './getConstrainedTypeAtLocation'; export * from './getContextualType'; export * from './getDeclaration';