1905home
1905home

Reputation: 321

How to find missing Await on Async function calls in Node+Typescript+VSCode?

We've deployed bugs in our node app b/c we forgot to prefix async function calls with "await".

Example:

const getUsers = async () => db.query('SELECT * from Users');

const testMissingAwait = async () => {
  const users = getUsers(); // <<< missing await
  console.log(users.length);
};

testMissingAwait();

Is there an easy way to find async function calls missing the await keyword?

Failing that, how much effort would it be to write a Visual Studio Code extension that flags these automatically? (I'm up for tackling if anyone can point me in the right direction).

Upvotes: 22

Views: 9428

Answers (3)

bmaupin
bmaupin

Reputation: 15995

tslint is deprecated but typescript-eslint has a rule for this: no-floating-promises

This rule forbids usage of Promise-like values in statements without handling their errors appropriately ... Valid ways of handling a Promise-valued statement include awaiting, returning, and either calling .then() with two arguments or .catch() with one argument.

To use it, you'll need to set up typescript-eslint:

  1. Install dependencies

    npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
    
  2. Modify your .eslintrc file

    These are the minimum required settings; see the documentation for more options (https://typescript-eslint.io/docs/linting/linting):

    {
      "parser": "@typescript-eslint/parser",
      "parserOptions": { "project": "./tsconfig.json" },
      "plugins": ["@typescript-eslint"],
      "rules": {
        "@typescript-eslint/no-floating-promises": ["error"]
      }
    }
    

If there are places where you want to call an async function without using await, you can either:

  • Use the void operator as mentioned in the documentation for the rule, e.g.

    void someAsyncFunction();
    
  • Or just change error to warn in the .eslintrc configuration above

Next time you run eslint you should see the rule applied:

$ npm run lint
...
./src/database/database.ts
  219:7  warning  Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator  @typescript-eslint/no-floating-promises

Since you mentioned VS Code specifically, this also integrates great with the ESLint plugin:

vscode showing no-floating-promises rule

Upvotes: 4

meziantou
meziantou

Reputation: 21337

TypeScript compiler doesn't provide a compiler option for that. However, TSLint 4.4 provides an option to detect floating promises. You can read this blog post for more detailed answer: Detect missing await in typescript

Download TSLint:

npm install -g tslint typescript

Configure TSLint:

{
    "extends": "tslint:recommended",
    "rules": {
        "no-floating-promises": true
    }
}

Run TSLint:

tslint --project tsconfig.json

If there are floating promises in your code, you should see the following error:

ERROR: F:/Samples/index.ts[12, 5]: Promises must be handled appropriately

Upvotes: 6

Fenton
Fenton

Reputation: 250942

TypeScript already does this

// users is a Promise<T>
const users = getUsers(); // <<< missing await

// users.length is not a property of users... then and catch are
console.log(users.length);

You can find situations where you won't be told about your mistake - where the types are compatible, for example I missed an await here:

function delay(ms: number) {
    return new Promise<number>(function(resolve) {
        setTimeout(() => {
            resolve(5);
        }, ms);
    });
}


async function asyncAwait() {
    let x = await delay(1000);
    console.log(x);

    let y = delay(1000);
    console.log(y);

    return 'Done';
}

asyncAwait().then((result) => console.log(result));

Because console.log doesn't cause any type incompatibility between my numbers and my promises, the compiler can't tell I have made a mistake.

The only solution here would be a type annotation... but if you're gonna forget an await, you're just as likely to forget a type annotation.

let y: number = delay(1000);

Upvotes: 3

Related Questions