I am struggling to grasp how types are automatically determined in Express
routes when utilizing multiple middlewares.
To conduct validation using zod
, I have employed the middleware package express-zod-safe
, although a similar issue arose with alternative middleware like zod-express-middleware
.
One of my middlewares, auth
, ideally should execute first to prevent unauthorized access. However, before implementing it, I need to perform validation
to ensure accurate type inference:
import express from "express";
import validate from "express-zod-safe";
import { z } from "zod";
const app = express();
export function checkAuth<
T extends express.Request,
U extends express.Response,
V extends express.NextFunction
>() {
return (req: T, res: U, next: V) => {
next();
};
}
const schema = z.object({ id: z.coerce.number().min(0) });
app.get(
"/api/product/:id",
validate({ params: schema }), // This line should be after checkAuth
checkAuth(),
(req, res) => {
req.params.test; // The Typescript server only rightfully complains when I validate first
res.send("Here's a product");
}
);
app.listen(3000, () => console.log("Server running on port 3000"));
Edit: As I plan to incorporate additional routes
with varying params
, I aim for the TypeScript server to automatically infer these types while keeping the checkAuth
function completely generic
.
Edit 2: Initially, I managed to prioritize checkAuth
by assigning any
to the request type, but this resulted in losing the Request
typing within it. How can I merge both types seamlessly?
export function checkAuth() {
return (req: any, res: any, next: any) => {
const authorizationHeader = req.headers.authorization;
next();
};
}
Edit 3: Following some adjustments using never
, I achieved correct typing for both functions:
export function checkAuth() {
return (req: express.Request<never>, res: any, next: any) => {
const authorizationHeader = req.headers.authorization;
next();
};
}
However, I remain unsure about the functioning behind this solution. It seems like postponing type inference to the subsequent function, while any
neglects all further typing. Is this the expected behavior? Any recommended resources for understanding TypeScript typing better?