Having some experience with TypeScript but being new to Express.js, I want to create a generic error handler in my Express.js app written in TypeScript. The following code works in JavaScript:
// catch 404 and forward to error handler
app.use((req, res, next) => {
next(new createError[404]());
});
// error handler
app.use((err, req, res, next) => {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
When trying to compile this with TypeScript however, it does not like the anonymous function in the second app.use call:
error TS7006: Parameter 'err' implicitly has an 'any' type.
app.use((err, req, res, next) => {
~~~
error TS7006: Parameter 'req' implicitly has an 'any' type.
app.use((err, req, res, next) => {
~~~
error TS7006: Parameter 'res' implicitly has an 'any' type.
app.use((err, req, res, next) => {
~~~
error TS7006: Parameter 'next' implicitly has an 'any' type.
app.use((err, req, res, next) => {
~~~~
Apparently, TypeScript was able to infer the type information of the parameters in the anonymous function for the first app.use call. It was not able to infer it for the second one though.
When changing the line into the following, TypeScript at least does not spit out any errors anymore... but eslint is angry with me because now I'm using implicit anys:
app.use((err: any, req: any, res: any, next: any) => {
Alternatively, Visual Studio Code allows me to apply a quick fix "Infer parameter types from usage" on the second anonymous function. This turns the second app.use line into this beauty (line breaks added by me):
app.use((err: { message: any; status: any; },
req: { app: { get: (arg0: string) => string; }; },
res: { locals: { message: any; error: any; };
status: (arg0: any) => void;
render: (arg0: string) => void; },
next: any) => {
While this would do its job, I feel that this beast is pretty much unmaintainable and incomprehensible.
So now I wonder: How would you implement something like this without sacrificing maintainability and comprehensibility?


@types/express-serve-static-core, which@types/expressdepends on.@types/expressinstalled. So why can'ttscautomatically infer this type definition from my code?