how to extend Request type in Express with custom property

Viewed 2488

I am trying to set up a custom middleware function in Node.js with typescript, where I want to store a decoded json web token into a custom request property 'user' like this

  function auth(req: Request, res: Response, next: NextFunction) {

     const decodedToken = jwt.verify(token, config.get("secret"));

     req.user = decodedToken.user; 
     next();
  }

but I keep getting a compiler error in the terminal

error TS2339: Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs>'.

I found several answers on here that suggested creating a custom.d.ts file and extend the Request type which I did like this:

namespace Express {
  interface Request {
    user?: string;
  }
}

now, the VS Code is happy, I get no linting, I get the correct intellisense and autocompletion and it does not let me assign a number, because it understands it needs to be a string. So it obviously works. However, when I run the code the terminal keeps giving me the same error and insists that property 'user' does not exist.

I tried restarting the TS server but that did not help.

I can make it work by creating a custom interface which extends the Request type and then using that, but I really want to know how to do it with the original Request type.

3 Answers

There's no way to do it on the original object with typescript - that's coming from Express and you're importing it from Express. The best you can do there is extend it like you have, or ignore the errors.

You definitely can do it in theory - you can add whatever you want to the req object in a middleware because it's just that - a javascript object. The warning you're getting is from typescript, not Express.

In practice, what you'd normally do is use res.locals for something like this. In typescript, it's defined as locals: Record<string, any>. So you could set res.locals.user in your middleware and then access it later wherever you want.

I'm not sure if I'm understanding this quite right, but, I think that the best solution is to create a custom Request class, adding this property.

export class CRequest extends Request {
    user?: string
    //add stuff to constructor etc
}

Now, you want to make it an interface, just replace class with interface I also do not reccomend you use the same name as Express & Request

I managed to make it work after a lot of googling and playing with folder structure and tsconfig.json file. I put the custom.d.ts file in ./@types/express (the sub-folder has to be there) and set the typeRoots in tsconfig.json like this:

compilerOptions: {
  "typeRoots": ["./@types"],
}

I also had to declare the namespace in custom.d.ts

declare namespace Express {
  interface Request {
    user: any;
  }
}

Now it compiles without error

Related