My goal is to define a custom Express endpoint wrapper, so it wraps async functions and handlers error accordingly (in the future it might do more stuff).
The handler is pretty basic, and I've defined it as such. It works as expected for me:
import type {Request, RequestHandler, Response} from 'express';
type Handler = (
fn: (req: Request, res: Response) => Promise<void> | void
) => RequestHandler;
const handler: Handler = fn => async (req, res, next) => {
try {
await fn(req, res);
} catch (e) {
next(e);
}
};
Now, I've found that router methods in Express are generic functions accepting type parameters, which can be used to define the shape of the response object, such as below. This also works as expected:
import express from 'express';
const app = express();
app.get<{}, {data: number}>(
'/posts',
(_req, res) => {
// @ts-expect-error Argument of type '{ hello: string; }' is not assignable to parameter of type '{ data: number; }'.
res.send({hello: 'world'});
}),
);
However, when I wrap the endpoint handler with my custom Handler, it does not work; the compiler does not complain. I can't figure out how/why. I've tried changing the definition of the Handler type, but it still doesn't work. Anyone has a clue on how I can make it work?
Here's the full code:
import express from 'express';
import type {Request, RequestHandler, Response} from 'express';
type Handler = (
fn: (req: Request, res: Response) => Promise<void> | void
) => RequestHandler;
const handler: Handler = fn => async (req, res, next) => {
try {
await fn(req, res);
} catch (e) {
next(e);
}
};
const app = express();
app.get<{}, {data: number}>(
'/posts',
handler((_req, res) => {
// @ts-expect-error Argument of type '{ should_not: string; }' is not assignable to parameter of type '{ data: number; }'.
res.send({should_not: 'compile'});
})
);
app.listen(4000, () => console.log('Listening on port 4000...'));
I've tried shuffling and declaring the Handler type definition a bunch of times, but nothing works. The compiler simply does not complain.
By wrapping the handler, you would also override the types, and the type for the
ResBodychanged intoany(when you do not specify the response type. the default type is used which is any). That's why the type checking does not work properly, andts-expect-errorshows youUnused '@ts-expect-error' directive.To fix this, you need to also consider the
ResBodyin the custom handler, and tell the express use defined custom type instead ofany.