diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index aa225d4e0aa2..b791fe150584 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -6,14 +6,17 @@ import * as ts from 'typescript'; import { createRule, + getConstrainedTypeAtLocation, getFunctionHeadLoc, getParserServices, isArrayMethodCallWithPredicate, isFunction, + isPromiseLike, isRestParameterDeclaration, nullThrows, NullThrowsReasons, } from '../util'; +import { parseFinallyCall } from '../util/promiseUtils'; export type Options = [ { @@ -360,6 +363,13 @@ export default createRule({ function checkArguments( node: TSESTree.CallExpression | TSESTree.NewExpression, ): void { + if ( + node.type === AST_NODE_TYPES.CallExpression && + isPromiseFinallyMethod(node) + ) { + return; + } + const tsNode = services.esTreeNodeToTSNodeMap.get(node); const voidArgs = voidFunctionArguments(checker, tsNode); if (voidArgs.size === 0) { @@ -563,6 +573,18 @@ export default createRule({ } } + function isPromiseFinallyMethod(node: TSESTree.CallExpression): boolean { + const promiseFinallyCall = parseFinallyCall(node, context); + + return ( + promiseFinallyCall != null && + isPromiseLike( + services.program, + getConstrainedTypeAtLocation(services, promiseFinallyCall.object), + ) + ); + } + function checkClassLikeOrInterfaceNode( node: | TSESTree.ClassDeclaration diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts index 5f7f1faf8bb4..dc671e8869d6 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -1096,6 +1096,13 @@ declare const useCallback: unknown>( ) => T; useCallback(async () => {}); `, + ` +Promise.reject(3).finally(async () => {}); + `, + ` +const f = 'finally'; +Promise.reject(3)[f](async () => {}); + `, ], invalid: [