Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript/Eslint throwing a 'Promise returned' error on a Express Router async route

I have the following endpoint setup to reset a database after test runs:

import { getConnection } from 'typeorm';
import express from 'express';
const router = express.Router();

const resetDatabase = async (): Promise<void> => {
  const connection = getConnection();
  await connection.dropDatabase();
  await connection.synchronize();
};

// typescript-eslint throws an error in the following route:
router.post('/reset', async (_request, response) => {
  await resetTestDatabase();
  response.status(204).end();
});

export default router;

The entire route since async is underlined with a typescript-eslint error Promise returned in function argument where a void return was expected.

The app works perfectly but I'm not sure if I should be doing a safer implementation or just ignoring/disabling Eslint for this one. Any idea of what's wrong with that code?

like image 694
HigoChumbo Avatar asked Apr 15 '21 18:04

HigoChumbo


People also ask

What is the return await rule in ESLint?

This rule aims to prevent a likely common performance hazard due to a lack of understanding of the semantics of async function. If you do not want the performance benefit of avoiding return await If you want the functions to show up in stack traces (useful for debugging purposes) This rule was introduced in ESLint v3.10.0.

What is the ESLint-plugin-no-floating-promise rule?

As mentioned above, the eslint-plugin-no-floating-promise rule is a great way to make sure you don't accidentally forget to use await in your async functions. If you're working in JavaScript and your project already uses eslint, adding eslint-plugin-no-floating-promise is as easy as adding the plugin to your .eslintrc config file:

Where can I find the tutorial for express-typescript?

Just as before, the repository for the tutorial is mwanago/express-typescript. If you find it helpful, feel free to give it a star. In the previous part of the tutorial, we wrote a handler function to return a post with a specific ID:

What is the use of return await in async?

Using return await inside an async function keeps the current function in the call stack until the Promise that is being awaited has resolved, at the cost of an extra microtask before resolving the outer Promise. return await can also be used in a try/catch statement to catch errors from another function that returns a Promise.


2 Answers

It seems you are using the no-misused-promises rule which states that you cannot return Promise<void> in a place where void is expected.

This means that you cannot return Promise<void> from your Express handler because the return type of RequestHandler from the library specifies that the return type should be void. I would suggest that you change it to return Promise<Response> by adding a simple return keyword:

import { getConnection } from 'typeorm';
import express from 'express';
const router = express.Router();

const resetDatabase = async (): Promise<void> => {
  const connection = getConnection();
  await connection.dropDatabase();
  await connection.synchronize();
};

// typescript-eslint throws an error in the following route:
router.post('/reset', async (_request, response) => {
  await resetTestDatabase();
  return response.status(204).send();  // <----- return added here
});

export default router;

The other option would be to avoid using async/await:

router.post('/reset', (_request, response) => {
  resetDatabase().then(() => response.status(204).send());
});
like image 100
cdimitroulas Avatar answered Sep 20 '22 06:09

cdimitroulas


I found a solution that doesn't involves using then() and let you use the abstraction of async without getting cursed by the eslint, there's two solutions (but i recommend more the second one)

First Solution: Using "inside async"

This is basic using a async inside the void like this:

router.post('/reset', (_request, response) => {
    (async () => {
        await resetTestDatabase();
        response.status(204).end();
    })()
});

Second Solution (recommended): "Type Overlap"

The second option is to you use it as an async, as Always, but say "hey TypeScript, nothing is wrong here hehe" with the "as" keyword

import { RequestHandler } from 'express'

router.post('/reset', (async (_request, response) => {
    await resetTestDatabase();
    response.status(204).end();
}) as RequestHandler);
like image 35
KuryKat Avatar answered Sep 20 '22 06:09

KuryKat