I am trying to write authentication logic for my app. I wrote a middleware that verifies a token, and therefore I need req.id
. I had to write an interface that extends from express.Request
to be able to access id
(similar issue).
import { Request } from "express";
export interface IGetUserAuthInfoRequest extends Request {
id: number;
}
This is how I implement the interface in my middleware to be able to use the req.id
property.
export const verifyToken = (req: IGetUserAuthInfoRequest, res: Response, next: NextFunction) => {
req.id = 1; // do something with req.id
/**
* some more logic
**/
next();
}
When I try calling the middleware, TypeScript complains, I think this is because of the custom type.
app.get("/api/try/admin", verifyToken, (req: Request, res: Response) => {
res.json({message: "try admin"})
});
The error looks something like this.
Type '(req: IGetUserAuthInfoRequest, res: Response, next: NextFunction)
=> Response | void' is not assignable to type
'ErrorRequestHandler<ParamsDictionary, any, any, ParsedQs, Record<string, any>> |
RequestHandler<ParamsDictionary, any, any, ParsedQs, Record<...>>'.
How could I type this correctly (preferably without using any
)?
CodePudding user response:
Your custom middleware is not compatible with (not a subtype of) the base Express middleware type.
Your custom middleware's req
parameter is more specialized than the base middleware's Request
parameter. In type theory language, your custom middleware is NOT contravariant with the base Express middleware in their argument types. Function argument contravariance is a requirement for substitution/subtyping.
One solution to this is to turn off strictFunctionChecks
in tsconfig.json
to allow function parameter bivariance. Another way is to change your custom request parameter's type to make it compatible with the base type:
interface IGetUserAuthInfoRequest extends Request {
id?: number // making this optional will make your custom middleware type-compatible
}
With these workarounds, your custom middleware will be accepted by app.get
, BUT its customized req
parameter will NOT be propagated to succeeding middlewares and request/error handlers. That is, req.id
will still be type undefined
instead of number | undefined
in other places (it will only be typed inside your custom middleware).
I think the proper solution to this is to use declaration-merging and merge your custom Request
interface to the Express
namespace. Express exposes this feature for custom middlewares to use:
declare global {
namespace Express {
// These open interfaces may be extended in an application-specific manner via declaration merging.
// See for example method-override.d.ts (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/method-override/index.d.ts)
interface Request {}
interface Response {}
interface Application {}
}
}
To merge your custom middleware's Request
interface:
declare global {
namespace Express {
interface Request {
id?: number
}
}
}