2

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:

  1. Using interface extend. This works (kind of) but I get an overload error so I can't compile using 'tsc':

overload error

// index.d.ts file

import { IUser } from "../models/user";
import { Request } from "express";


export interface IUserRequest extends Request {
  user: IUser; // or any other type
}
  1. 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.

noahreeves
  • 43
  • 6

1 Answers1

1

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;
    }
  }
}
Dilshan
  • 2,797
  • 1
  • 8
  • 26
  • Hey thanks for the response. I made that change but it's not recognizing the req.user property. I am getting the same error as in my image "Unable to find req.user when using global namespace method" – noahreeves Dec 18 '22 at 11:39
  • try after remove "files" in tsconfig file – Dilshan Dec 18 '22 at 11:47
  • I removed "files" from my tsconfig but I'm still getting the same "Unable to find req.user" error. I even tried restarting the typescript server... :/ – noahreeves Dec 18 '22 at 12:05
  • Can you add a sandbox project so that I can take a look – Dilshan Dec 18 '22 at 12:37
  • Can you access this sandbox? https://codesandbox.io/p/github/noahreeves-1/my-blog-site/csb-fd63k1/draft/flamboyant-silence?selection=%5B%7B%22endColumn%22%3A25%2C%22endLineNumber%22%3A11%2C%22startColumn%22%3A25%2C%22startLineNumber%22%3A11%7D%5D&file=%2Fsrc%2Findex.ts If not I can give you my github repo. I don't really care about anything private because this is just a personal project as part of an online self-paced bootcamp I'm doing – noahreeves Dec 18 '22 at 15:54
  • [Remove](https://github.com/noahreeves-1/my-blog-site/pull/1/commits/204f2bd2cb286f407be31eb1d4d80a9c42d318ec) the `import { Request } from "express";` from `*.d.ts` file. You don't need that. Check the last link i added in the answer. – Dilshan Dec 18 '22 at 16:05
  • I appreciate your help man. I edited the index.d.ts file with your changes but I'm still getting the error: "'req.user' is possibly 'undefined'." and error: "Property 'username' does not exist on type 'User'." – noahreeves Dec 18 '22 at 18:29
  • Yes it can be undefined because of the `?` mark you used in `user?: IUser;`. That's true because sometimes user might not authenticated so in that case `req.user` can be undefined. You have to check if it is available before do any operation with it. `if(req.user) { }` – Dilshan Dec 19 '22 at 01:32
  • Thanks Dilshan! That got rid of the error "'req.user' is possibly undefined.' But I'm still left with the error on req.user.username (underline on username). It says 'Property 'username' does not exist on type "User".' This means that the *d.ts file isn't being set up or read properly right? – noahreeves Dec 19 '22 at 04:40
  • I went with your 2nd recommendation - I changed my index.d.ts so that interface User includes first_name, last_name, username, email, admin but my admin_code_post controller gives me an error saying "Property 'username' does not exist on type 'User'." "username" in req.user.username is underlined – noahreeves Dec 19 '22 at 05:23
  • Because you used `req: IUserRequest` in the controller. Change it to the default `req: Request` – Dilshan Dec 19 '22 at 05:39
  • Yeah, I made that change. The error occurred even with type as Request. Here's my up-to-date github repo for your info: https://github.com/noahreeves-1/my-blog-site – noahreeves Dec 19 '22 at 05:48
  • 1
    Check the last code sample of the answer. updated that. – Dilshan Dec 19 '22 at 06:10