Reputation: 2823
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 any
s:
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?
Upvotes: 1
Views: 1694
Reputation: 1380
The error you get is comming from the settings you have in tsconfig.json
"noImplicitAny": true
It is good practice to have strict typescript. This is how I handle the errors and this are the types for each argument. Install this package
npm i -D @types/express
import { NextFunction, Request, Response } from 'express';
...
this.app.use((error: unknown, req: Request, res: Response, next: NextFunction): void => {
if (error instanceof HttpException) {
res.status(error.code).send({ message: error.message });
return;
}
if (error instanceof Error && error.message) {
if (error.message === 'Method not implemented') {
res.status(501).send({ message: httpStatusCode(501) });
return;
}
res.status(500).send({ message: error.message });
return;
}
res.status(500).send({ message: httpStatusCode(500) });
return;
});
And this is the HttpException class if you wonder what is
export class HttpException {
constructor(public code: number, public message: string = httpStatusCode(code)) {}
}
With this setup in conjuntion with an async handler for the middleware you can throw errors or even have promises that fails and the errors are handled in one place.
Upvotes: 2