I am writing a user auth middleware based on Jason Watmore's user auth boilerplate.
My expected result is that this code "just works" because it's copied from another project where it does exactly that.
My actual result is that I get this long error message:
(alias) authorize(roles?: Role[]): ({
(req: express.Request<ParamsDictionary, any, any, QueryString.ParsedQs, Record<string, any>>, res: express.Response<...>, next: express.NextFunction): Promise<...>;
unless: (options: Params) => {
...;
};
} | ((request: RequestWithUser, res: express.Response<...>, next: express.NextFunction) => Promise<...>))[]
import authorize
No overload matches this call.
The last overload gave the following error.
Argument of type '({ (req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: NextFunction): Promise<...>; unless: (options: Params) => { ...; }; } | ((request: RequestWithUser, res: Response<...>, next: NextFunction) => Promise<...>))[]' is not assignable to parameter of type 'RequestHandlerParams<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
Type '({ (req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: NextFunction): Promise<...>; unless: (options: Params) => { ...; }; } | ((request: RequestWithUser, res: Response<...>, next: NextFunction) => Promise<...>))[]' is not assignable to type '(ErrorRequestHandler<ParamsDictionary, any, any, ParsedQs, Record<string, any>> | RequestHandler<ParamsDictionary, any, any, ParsedQs, Record<...>>)[]'.
Type '{ (req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: NextFunction): Promise<...>; unless: (options: Params) => { ...; }; } | ((request: RequestWithUser, res: Response<...>, next: NextFunction) => Promise<...>)' is not assignable to type 'ErrorRequestHandler<ParamsDictionary, any, any, ParsedQs, Record<string, any>> | RequestHandler<ParamsDictionary, any, any, ParsedQs, Record<...>>'.
Type '(request: RequestWithUser, res: Response, next: NextFunction) => Promise<Response<any, Record<string, any>> | undefined>' is not assignable to type 'ErrorRequestHandler<ParamsDictionary, any, any, ParsedQs, Record<string, any>> | RequestHandler<ParamsDictionary, any, any, ParsedQs, Record<...>>'.
Type '(request: RequestWithUser, res: Response, next: NextFunction) => Promise<Response<any, Record<string, any>> | undefined>' is not assignable to type 'ErrorRequestHandler<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
Types of parameters 'res' and 'req' are incompatible.
Type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' is missing the following properties from type 'Response<any, Record<string, any>>': status, sendStatus, links, send, and 53 more.ts(2769)
index.d.ts(163, 5): The last overload is declared here.
No quick fixes available
Here is the offending code
function authorize(roles: Role[] = []) {
if (typeof roles === "string") {
roles = [roles];
}
return [
jwt({
secret, // authenticate JWT token and attach user to request object (req.user)
algorithms: ["HS256"],
}),
// problem is caused here with "RequestWithUser"
async (request: RequestWithUser, res: Response, next: NextFunction) => {
const acctInfo = request.auth;
if (acctInfo?.acctId === undefined) {
return res.status(401).json({ message: "Unauthorized" });
}
request.user = {
acctId: acctInfo.acctId,
role: "", // temp to satisfy ts
};
const account: Account | null = await acctDAO.getAccountById(acctInfo.acctId);
if (!account) return res.status(401).json({ message: "Unauthorized" });
const refreshTokens = await rtDAO.getAllRefreshTokensForAccount(account.acctId);
const validRoles = Object.values(Role);
const acctRole: Role = account.role as Role;
const rolesFoundOnRequest = roles.length;
if (rolesFoundOnRequest && !validRoles.includes(acctRole)) {
return res.status(401).json({ message: "Unauthorized" });
}
request.user.role = account.role;
request.user.ownsToken = (token: string) => !!refreshTokens.find((x: any) => x.token === token);
next();
},
];
}
export default authorize;
When I change RequestWithUser back to just Request, the error goes away, but that doesn't work because then the rest of the middleware will have TS errors about the wrong type being expected. so it has to be this way.
As you can see, RequestWithUser is just an Express Request extended:
export interface RequestWithUser extends Request {
user?: {
role: string;
ownsToken?: Function;
acctId: number;
};
auth?: {
sub: any;
acctId: number;
};
}
I don't understand this error message at all. It seems to be saying "express will pass this to ErrorRequestHandler and the shapes don't fit" but I'm not clear at all what's up.
edit: so obviously I should tell you where the error appears. The following routes are all examples of where it appears.
this.router.get("/", authorize([Role.Admin]), this.getAllAccounts);
this.router.get("/:id", authorize(), this.getAccountById);
this.router.post("/", authorize([Role.Admin]), createAccountSchema, this.createAccount);
this.router.put("/:id", authorize(), updateRoleSchema, this.updateAccount);
RequestWithUseris just an ExpressRequestextended
Instead of creating a specific interface (even if it extends Request), use declaration merging to augment the actual Request interface itself. See e.g. Extend Express Request object using Typescript
The issue is not in the runtime code, but just on the extra typing.
The error message itself is indeed misleading, but it is directly caused by the different 1st parameter type.
Because it does not match the usual Request type, TypeScript tries to infer a different function overload, and ends up with one that should accept a mix of normal RequestHandler and ErrorRequestHandler... but your functions cannot handle both, hence the error message.
I was facing this error and fix this by addin a : Promise<any> as return type.
My bad code was:
app.get(
'/',
[
// ...some middlewares
],
(req: Request, res: Response)=>{
// some code
return res.json({msg: 'ok'});
}
);
Then I add a return type to my function:
app.get(
'/',
[
// ...some middlewares
],
(req: Request, res: Response):Promise<any>=>{
// some code
return res.json({msg: 'ok'});
}
)
And that fix my problem after some headake.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With