I'm trying to extend Express' Request object so that I can access req.user, but I keep running into an overload error. I tried both of these solutions here but they don't work for me.
EDIT: I am using passport.js and jwt
Extend Express Request object using Typescript
Unable to extend Express Request in TypeScript
I have tried extending Express' Request object in different two ways:
- Using interface extend. This works (kind of) but I get an overload error so I can't compile using 'tsc':
// index.d.ts file
import { IUser } from "../models/user";
import { Request } from "express";
export interface IUserRequest extends Request {
user: IUser; // or any other type
}
- Creating a global namespace. This does not work because I am unable to access the req.user property. I have tried importing { Request } from "express" but this doesn't work for this option because it says Request is declared but never used:
//index.d.ts file
import { IUser } from "../models/user";
// import { Request } from "express"
export {};
declare global {
namespace Express {
interface Request {
user?: IUser;
}
}
}
This is my post controller that's trying to access req.user:
export const admin_code_post = [
body("admin_code", "Wrong code buddy")
.trim()
.escape()
.custom((value: string) => {
if (value !== process.env.SECRET_CODE) {
throw new Error("Admin code is incorrect");
}
return true;
}),
(req: IUserRequest, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
res.render("admin_code", {
title: "Enter Code",
errors: errors.array(),
user: req.user,
});
return;
}
User.findOneAndUpdate(
{ username: req.user?.username }, // issue here if I create a global namespace
{ admin: true },
(err: Error, user: IUser) => {
if (err) return next(err);
res.render("index", {
title: "Welcome",
user,
});
}
);
},
];
Unable to find req.user when using global namespace method
This is my tsconfig.json :
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"rootDir": "./src",
"typeRoots": [
"./node_modules/@types",
"./types"
],
"sourceMap": true,
"outDir": "dist",
"noEmitOnError": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitAny": true
},
"include": ["./src/**/*"],
"exclude": ["node_modules"],
"ts-node": {
"files": true
},
"files": [
"./src/types/index.d.ts"
]
}
What am I doing wrong? Any advice would be appreciated.
CodePudding user response:
Your 2nd approach should work with the following changes,
import { IUser } from "../models/user";
// export {}; <-- you don't need this
declare global {
namespace Express {
export interface Request { // <-- you have to export the interface
user?: IUser;
}
}
}
Make sure this code is in a *.d.ts
file. Please check the documentation to learn more about how namespaces & declaration merging works
UPDATE
Just noticed that you use passport js. If you check the index.d.ts
file of the passport js you can find following code lines,
declare global {
namespace Express {
// tslint:disable-next-line:no-empty-interface
interface AuthInfo {}
// tslint:disable-next-line:no-empty-interface
interface User {} // <-- empty interface
interface Request {
authInfo?: AuthInfo | undefined;
user?: User | undefined; // <-- using that as the type of req.user
so the one we define won't work. You can try,
const user = req.user as IUser
example
or you can change your index.d.ts
as,
// see - https://stackoverflow.com/a/63973683/11306028
export {};
declare global {
namespace Express {
interface User {
first_name: string;
last_name: string;
username: string;
email: string;
admin: boolean;
}
}
}