Skip to main content

return-await

Enforce consistent awaiting of returned promises.

🔧

Some problems reported by this rule are automatically fixable by the --fix ESLint command line option.

💡

Some problems reported by this rule are manually fixable by editor suggestions.

💭

This rule requires type information to run.

This rule builds on top of the eslint/no-return-await rule. It expands upon the base rule to add support for optionally requiring return await in certain cases.

The extended rule is named return-await instead of no-return-await because the extended rule can enforce the positive or the negative. Additionally, while the core rule is now deprecated, the extended rule is still useful in many contexts:

How to Use

.eslintrc.cjs
module.exports = {
"rules": {
// Note: you must disable the base rule as it can report incorrect errors
"no-return-await": "off",
"@typescript-eslint/return-await": "error"
}
};

Try this rule in the playground ↗

Options

See eslint/no-return-await options.

type Options = 'in-try-catch' | 'always' | 'never';

const defaultOptions: Options = 'in-try-catch';

in-try-catch

In cases where returning an unawaited promise would cause unexpected error-handling control flow, the rule enforces that await must be used. Otherwise, the rule enforces that await must not be used.

Listing the error-handling cases exhaustively:

  • if you return a promise within a try, then it must be awaited, since it will always be followed by a catch or finally.
  • if you return a promise within a catch, and there is no finally, then it must not be awaited.
  • if you return a promise within a catch, and there is a finally, then it must be awaited.
  • if you return a promise within a finally, then it must not be awaited.
  • if you return a promise between a using or await using declaration and the end of its scope, it must be awaited, since it behaves equivalently to code wrapped in a try block.
async function invalidInTryCatch1() {
try {
return Promise.reject('try');
} catch (e) {
// Doesn't execute due to missing await.
}
}

async function invalidInTryCatch2() {
try {
throw new Error('error');
} catch (e) {
// Unnecessary await; rejections here don't impact control flow.
return await Promise.reject('catch');
}
}

// Prints 'starting async work', 'cleanup', 'async work done'.
async function invalidInTryCatch3() {
async function doAsyncWork(): Promise<void> {
console.log('starting async work');
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('async work done');
}

try {
throw new Error('error');
} catch (e) {
// Missing await.
return doAsyncWork();
} finally {
console.log('cleanup');
}
}

async function invalidInTryCatch4() {
try {
throw new Error('error');
} catch (e) {
throw new Error('error2');
} finally {
// Unnecessary await; rejections here don't impact control flow.
return await Promise.reject('finally');
}
}

async function invalidInTryCatch5() {
return await Promise.resolve('try');
}

async function invalidInTryCatch6() {
return await 'value';
}

async function invalidInTryCatch7() {
using x = createDisposable();
return Promise.reject('using in scope');
}
Open in Playground

always

Requires that all returned promises are awaited.

Examples of code with always:

async function invalidAlways1() {
try {
return Promise.resolve('try');
} catch (e) {}
}

async function invalidAlways2() {
return Promise.resolve('try');
}

async function invalidAlways3() {
return await 'value';
}
Open in Playground

never

Disallows all awaiting any returned promises.

Examples of code with never:

async function invalidNever1() {
try {
return await Promise.resolve('try');
} catch (e) {}
}

async function invalidNever2() {
return await Promise.resolve('try');
}

async function invalidNever3() {
return await 'value';
}
Open in Playground

When Not To Use It

Type checked lint rules are more powerful than traditional lint rules, but also require configuring type checked linting. See Performance Troubleshooting if you experience performance degredations after enabling type checked rules.

Resources

Taken with ❤️ from ESLint core.