Even though I love Lin Du's answer, It might not work for usecases where you may have different Session type for different requests (one for user facing, one for admin panel, etc. -- In this case the session object is not constant)
I ended up creating CustomRequestHandlers to handle different type of Session types instead.
import session from 'express-session';
import { ParamsDictionary } from 'express-serve-static-core';
import { ParsedQs } from 'qs';
import { NextFunction, Request, RequestHandler, Response } from 'express';
interface CustomUserSession extends session.Session {
user: {
username: string;
};
}
interface RequestWithSession<
P = ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = ParsedQs,
LocalsObj extends Record<string, any> = Record<string, any>,
> extends Request<P, ResBody, ReqBody, ReqQuery, LocalsObj> {
session: CustomUserSession;
}
export interface UserReqHandler<
P = ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = ParsedQs,
LocalsObj extends Record<string, any> = Record<string, any>,
> extends RequestHandler {
(req: RequestWithSession<P, ResBody, ReqBody, ReqQuery, LocalsObj>, res: Response<ResBody, LocalsObj>, next: NextFunction): void;
}
Once this was done I would just use it this way
export const handleUserAuth: UserReqHandler = async (req, res, next) => {
req.session.user = {
username: 'foo', // No Type errors.
};
}
We can then create multiple Req Handlers based on our requirements.