diff --git a/packages/eslint-plugin/src/rules/await-thenable.ts b/packages/eslint-plugin/src/rules/await-thenable.ts index fca9fd83de00..f5932dd7f259 100644 --- a/packages/eslint-plugin/src/rules/await-thenable.ts +++ b/packages/eslint-plugin/src/rules/await-thenable.ts @@ -1,3 +1,4 @@ +import type { TSESLint } from '@typescript-eslint/utils'; import * as tsutils from 'ts-api-utils'; import * as util from '../util'; @@ -10,8 +11,10 @@ export default util.createRule({ recommended: 'recommended', requiresTypeChecking: true, }, + hasSuggestions: true, messages: { await: 'Unexpected `await` of a non-Promise (non-"Thenable") value.', + removeAwait: 'Remove unnecessary `await`.', }, schema: [], type: 'problem', @@ -35,6 +38,23 @@ export default util.createRule({ context.report({ messageId: 'await', node, + suggest: [ + { + messageId: 'removeAwait', + fix(fixer): TSESLint.RuleFix { + const sourceCode = context.getSourceCode(); + const awaitKeyword = util.nullThrows( + sourceCode.getFirstToken(node, util.isAwaitKeyword), + util.NullThrowsReasons.MissingToken( + 'await', + 'await expression', + ), + ); + + return fixer.remove(awaitKeyword); + }, + }, + ], }); } }, diff --git a/packages/eslint-plugin/tests/rules/await-thenable.test.ts b/packages/eslint-plugin/tests/rules/await-thenable.test.ts index 4b51a75ac8bb..27e4309092e8 100644 --- a/packages/eslint-plugin/tests/rules/await-thenable.test.ts +++ b/packages/eslint-plugin/tests/rules/await-thenable.test.ts @@ -1,4 +1,4 @@ -import { RuleTester } from '@typescript-eslint/rule-tester'; +import { noFormat, RuleTester } from '@typescript-eslint/rule-tester'; import rule from '../../src/rules/await-thenable'; import { getFixturesRootDir } from '../RuleTester'; @@ -202,33 +202,83 @@ const doSomething = async ( invalid: [ { - code: ` -async function test() { - await 0; - await 'value'; - - await (Math.random() > 0.5 ? '' : 0); - - class NonPromise extends Array {} - await new NonPromise(); -} - `, + code: 'await 0;', errors: [ { - line: 3, + line: 1, + messageId, + suggestions: [ + { + messageId: 'removeAwait', + output: ' 0;', + }, + ], + }, + ], + }, + { + code: "await 'value';", + errors: [ + { + line: 1, messageId, + suggestions: [ + { + messageId: 'removeAwait', + output: " 'value';", + }, + ], }, + ], + }, + { + code: "async () => await (Math.random() > 0.5 ? '' : 0);", + errors: [ { - line: 4, + line: 1, messageId, + suggestions: [ + { + messageId: 'removeAwait', + output: "async () => (Math.random() > 0.5 ? '' : 0);", + }, + ], }, + ], + }, + { + code: noFormat`async () => await(Math.random() > 0.5 ? '' : 0);`, + errors: [ { - line: 6, + line: 1, messageId, + suggestions: [ + { + messageId: 'removeAwait', + output: "async () => (Math.random() > 0.5 ? '' : 0);", + }, + ], }, + ], + }, + { + code: ` +class NonPromise extends Array {} +await new NonPromise(); + `, + errors: [ { - line: 9, + line: 3, messageId, + suggestions: [ + { + messageId: 'removeAwait', + output: ` +class NonPromise extends Array {} + new NonPromise(); + `, + }, + ], }, ], }, @@ -247,58 +297,84 @@ async function test() { { line: 8, messageId, + suggestions: [ + { + messageId: 'removeAwait', + output: ` +async function test() { + class IncorrectThenable { + then() {} + } + const thenable = new IncorrectThenable(); + + thenable; +} + `, + }, + ], }, ], }, { code: ` -const doSomething = async ( - obj1: { a?: { b?: { c?: () => void } } }, - obj2: { a?: { b?: { c: () => void } } }, - obj3: { a?: { b: { c?: () => void } } }, - obj4: { a: { b: { c?: () => void } } }, - obj5: { a?: () => { b?: { c?: () => void } } }, - obj6?: { a: { b: { c?: () => void } } }, - callback?: () => void, -): Promise => { - await obj1.a?.b?.c?.(); - await obj2.a?.b?.c(); - await obj3.a?.b.c?.(); - await obj4.a.b.c?.(); - await obj5.a?.().b?.c?.(); - await obj6?.a.b.c?.(); - - await callback?.(); -}; +declare const callback: (() => void) | undefined; +await callback?.(); `, errors: [ { - line: 11, - messageId, - }, - { - line: 12, - messageId, - }, - { - line: 13, - messageId, - }, - { - line: 14, - messageId, - }, - { - line: 15, + line: 3, messageId, + suggestions: [ + { + messageId: 'removeAwait', + output: ` +declare const callback: (() => void) | undefined; + callback?.(); + `, + }, + ], }, + ], + }, + { + code: ` +declare const obj: { a?: { b?: () => void } }; +await obj.a?.b?.(); + `, + errors: [ { - line: 16, + line: 3, messageId, + suggestions: [ + { + messageId: 'removeAwait', + output: ` +declare const obj: { a?: { b?: () => void } }; + obj.a?.b?.(); + `, + }, + ], }, + ], + }, + { + code: ` +declare const obj: { a: { b: { c?: () => void } } } | undefined; +await obj?.a.b.c?.(); + `, + errors: [ { - line: 18, + line: 3, messageId, + suggestions: [ + { + messageId: 'removeAwait', + output: ` +declare const obj: { a: { b: { c?: () => void } } } | undefined; + obj?.a.b.c?.(); + `, + }, + ], }, ], },