I've been having a real difficult time trying to define custom middleware for our app. I'm using [email protected] and [email protected].
All the examples I've been seeing for typing middleware implies that the user is not adding anything to the req
or res
argument, but I know that that was kind of the point of middleware - mutate either one of those objects knowing that the middlewares had to be loaded in a certain order (ie. cookie-parser or body-parser was notorious for this in the early days, i guess still is?). Anyway, most of the examples net out to something like this github issue: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/26146#issuecomment-393386416
The problem I have with that is that we do mutate the req
and res
objects being passed along to each middleware so I've had to type both objects accordingly:
import {
Express as ExpressInterface,
Request as ExpressRequest,
Response as ExpressResponse,
NextFunction as ExpressNextFunction,
} from 'express';
import LoggerType from 'some-log-package';
// don't do anything different the `next()`
export type ExpressNextFunction = ExpressNextFunction;
export interface CustomServerApp extends ExpressInterface {
customHeader: string;
customLogger: LoggerType;
}
export interface CustomServerRequest extends ExpressRequest {
app: CustomServerApp,
id: string,
urlSearchParams: URLSearchParams,
// etc
}
export interface CustomServerResponse extends ExpressResponse { ... }
So I export the basic arguments for middleware:
req
=CustomServerRequest
res
=CustomServerResponse
next
=ExpressNextFunction
Of course, I import those accordingly to my middleware files:
import {
CustomServerRequest,
CustomServerResponse,
ExpressNextFunction
} from './server-types'
export const customMiddleware = (req: CustomServerRequest, res: CustomServerResponse, next: ExpressNextFunction): void {
// do something here with either `req` or `res` or both!
next()
};
We have a centralized middleware.ts
file where we put all of our global middleware into so it looks something like this:
import { CustomServerApp } from './server-types';
import { customMiddleware } from './middlewares/customMiddleware';
export const middlewares = (app: CustomServerApp): void => {
app.use(
customMiddleware,
// maybe more middlewares, etc
);
}
However, then the type checker runs, I get errors like the following:
No overload matches this call.
The last overload gave the following error.
Argument of type '(req: Request, res: Response, next: NextFunction) => void' is not assignable to parameter of type 'PathParams'.
Type '(req: Request, res: Response, next: NextFunction) => void' is missing the following properties from type '(string | RegExp)[]': pop, push, concat, join, and 27 more.ts(2769)
I've been having a real tough time understanding why this is getting caught on the last overload definition. Just based on the @types/express
, it seems hard to add an additional overload to middleware handler even with type declaration merging.
Even when I follow a similar pattern to the core express types to define a middleware handler - https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express-serve-static-core/index.d.ts#L40-L43 - it doesn't work out either. It also doesn't seem like it's easy to extend
from either - https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express/index.d.ts#L99 - since you can't pass a generic to the core RequestHandler, unlike the router definitions (IRouterHandler
, IRouterMatcher
). When I try and type it this way, I get different, but similar type errors.
Has anyone else experienced doing something similar?