Aquila169 / zod-express-middleware

Express middleware to validate requests using zod schema's.
MIT License
86 stars 13 forks source link

Types of parameters 'req' and 'req' are incompatible #4

Closed Drarig29 closed 2 years ago

Drarig29 commented 2 years ago

I had the following route, which was compiling perfectly:

app.post(
  '/example',
  processRequest(mySchema),
  handleExample,
);

But as soon as I add a middleware, in my case to check that the user is authenticated, the compilation breaks:

app.post(
  '/example',
+ checkUserAuth,
  processRequest(mySchema),
  handleExample,
);

The compilation error:

Error:(95, 5) TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type 'RequestHandler<{ myProperty: number; }, any, any, ParsedQs, Record<string, any>>' is not assignable to parameter of type 'RequestHandlerParams<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
      Type 'RequestHandler<{ myProperty: number; }, any, any, ParsedQs, Record<string, any>>' is not assignable to type 'RequestHandler<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
        Types of parameters 'req' and 'req' are incompatible.
          Type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' is not assignable to type 'Request<{ myProperty: number; }, any, any, ParsedQs, Record<string, any>>'.
            Property 'myProperty' is missing in type 'ParamsDictionary' but required in type '{ myProperty: number; }'.

Here is the signature of my middleware:

const checkUserAuth = (req: Request, res: Response, next: NextFunction) => {
  // code...
};
Drarig29 commented 2 years ago

I fixed this by removing the ParamsDictionary, which was too losely typed to be compatible with { myProperty: number; }.

To do this, you can specify a custom parameter to Express's Request:

- const checkUserAuth = (req: Request, res: Response, next: NextFunction) => { }
+ const checkUserAuth = (req: Request<unknown>, res: Response, next: NextFunction) => { }
Drarig29 commented 2 years ago

Okay, little follow-up. The fact that I only needed to have one unknown was because my custom schema was only specifying params. But I added a query in my schema and it broke again.

This time with the error:

Property 'myQueryProperty' is missing in type 'ParsedQs' but required in type '{ myQueryProperty: number; }'.

So I needed to have Request<unknown, unknown, unknown, unknown> instead, to be sure. And it's not a problem semantically because in such a middleware, we actually don't know a lot about a request, and we don't want to.

I created a new type not to have to repeat this everywhere:

import { Request } from 'express';

export type AnyRequest = Request<unknown, unknown, unknown, unknown>

So I finally have:

- const checkUserAuth = (req: Request, res: Response, next: NextFunction) => { }
+ const checkUserAuth = (req: AnyRequest, res: Response, next: NextFunction) => { }
foofel commented 1 year ago

I seem to have the same (or similar error). If i add a session auth handler with the same signature you used I still get an untyped request. Removing the auth handler from the route fixes this. But i dont know how to fix the actual problem.