I've been facing challenges integrating custom Request types with TypeScript.
Within my application, I have both public and private routes.
The public routes utilize the Request
type from Express. On the other hand, the private routes make use of a custom PrivateRequest
type that extends the Request
type from Express, defined as follows:
import type { Request } from "express";
import type * as Cookies from "cookies";
export type PrivateRequest = Request & {
user: User;
cookies: Cookies;
}
The routing setup for public and private routes is as follows:
const publicRouter = express.Router();
const privateRouter = express.Router();
privateRouter.use([userSession]);
publicRouter.post("/login", login);
privateRouter.get("/api/user", user);
Here's an example of a private route that utilizes the PrivateRequest
type, and TypeScript doesn't raise any issues in this scenario:
export default async function user(req: PrivateRequest, res: Response) {
try {
res.json({ test: true });
} catch (err) {
console.error(err);
res.status(500).json({ errors: { server: "Server error" } });
}
}
The problem arises with the private routes, e.g.:
privateRouter.get("/api/user", user);
For the privately defined routes, TypeScript throws this specific error:
TS2769: No overload matches this call
How can I resolve this issue? I've tried various solutions, but nothing seems to work, and I'm unsure of the root cause.
I can eliminate this error by making the user
nullable on the PrivateRequest
, but this goes against the expected behavior since all private routes are supposed to have a user on the req
object. The userSession
middleware either returns a 401 error or adds the user to the req
object for subsequent private routes. Here's an example of what my userSession
middleware looks like:
export default async function userSession(
req: Request,
res: Response,
next: NextFunction
) {
try {
req.cookies = new Cookies(req, res);
// [...] authentication and user entity fetching (throws if either one fails)
if (user === null) {
res.status(401).json({ errors: { server: "Unauthorised" } });
} else {
// @ts-ignore
req.user = user;
next();
}
} catch {
res.status(401).json({ errors: { server: "Unauthorised" } });
}
}