DEV Community

Kwabena Bio Berko
Kwabena Bio Berko

Posted on • Updated on

Extend Express's Request Object with Typescript Declaration Merging.

Express has been my go to server side node web framework for the past few years. Its fast, unopinionated and so easy to get up and running. I really love using it along with Typescript too, I should say. It enhances code quality and understand-ability. Refactoring your code in Typescript is also much more easier and faster. Plus, you get the added advantage of code completion and IntelliSense when using modern text editors like Visual Studio Code. 😋

One of the concepts of Typescript which I've recently began using is Declaration Merging.
Declaration Merging allows you to merge two or more distinct declaration or types declared with the same name into a single definition. This concept allows you to attach your own custom property onto another Typescript interface type. Lets take a look at a typical Express middleware.

Alt Text

The above code is an Express middleware that is used to ensure that a user is authenticated when he or she tries to access a protected resource. It decodes the user's token from the authorization property of the request headers and attaches the user to the Request object. But see that red squiggly line?
Thats because the property currentUser does not exist on Express's Request interface type. Let's fix that. 😃

The first thing we need to do is to create a new declaration file @types > express > index.d.ts in the root of our project.

You would notice this is the exact same file name and path in our node_modules/@types folder. For Typescript declaration merging to work, the file name and its path must match the original declaration file and path.

Next we need to make some few changes in the project's tsconfig.json file. Let's update the typeRoots value to the following:

...
"typeRoots": [
      "@types",
      "./node_modules/@types",
    ]                    
...
Enter fullscreen mode Exit fullscreen mode

By default, the Typescript compiler looks for type definitions in the node_modules/@types folder. The above code instructs the compiler to look for type definitions in this folder as well as our custom @types folder in our project root.

It's now time to add our custom currentUser property to Express's Request interface type by modifying the index.d.ts file we created earlier:


import { UserModel } from "../../src/user/user.model";

declare global{
    namespace Express {
        interface Request {
            currentUser: UserModel
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Lets take a look again at our middleware file and we immediately notice that the red squiggly line is gone! This is because the Typescript compiler now recognizes the currentUser property as a valid property on the Request type interface.

Alt Text

Happy Coding, everyone!

Oldest comments (44)

Collapse
 
terabytes profile image
terabytes

VS code doesn't show error in editor but during compilation i get:
currentUser does not exist on type 'Request'.
Its Ts error.

Collapse
 
mehmetnyarar profile image
Mehmet N Yarar

same here. not only that, testing (jest/ts-jest) also fails unless I do:

declare module 'express' {
  export interface Request {
    i18n: import('i18next').i18n
    language: string
  }
}
Collapse
 
beadle85 profile image
Beadle

I am having the same issue. Did you every find a fix for this?

Collapse
 
tjsarkka profile image
TJSarkka

Thank you! I spent the last hours scratching my head and trying different combinations of config files and .d.ts files provided in other solutions.. and this was the only one that worked for me for both ts-node and tsc. :)

Collapse
 
kwabenberko profile image
Kwabena Bio Berko

You're welcome.
Glad to be of help.

Collapse
 
stevemu profile image
Qi Mu

Thank you so much!!!

Collapse
 
kwabenberko profile image
Kwabena Bio Berko

You're welcome. 😊

Collapse
 
mxgit1090 profile image
Mark Tsai • Edited

Similar methods to define Request types can also be found in the following libraries:

I also spent so much time resolving this issue, thank you for your article!

Collapse
 
kwabenberko profile image
Kwabena Bio Berko

You're welcome
Really glad you found it useful.

Collapse
 
renemaldonado profile image
Rene Maldonado Fimbres

Bro, have been having problems with this for 2 days.

Thanks for the info, I was missing the @ sign .

Collapse
 
kwabenberko profile image
Kwabena Bio Berko

You're welcome.

Collapse
 
andersonluis87 profile image
Anderson Luis Silva

Thank you very much!
Very well explained.

Collapse
 
kwabenberko profile image
Kwabena Bio Berko

Thank you. 😊

Collapse
 
hanslissi profile image
riejoc16

Oh wow! I was stuck for a while now trying to achieve exactly that. I couldn't find what I had done wrong. Only with this post I was able to get it done, thanks!

Collapse
 
kwabenberko profile image
Kwabena Bio Berko

You're welcome. 😄

Collapse
 
georgenorris profile image
George C. Norris

Getting a Augmentations for the global scope can only be directly nested in external modules or ambient module declarations. ??

Collapse
 
georgenorris profile image
George C. Norris

I've tried SOO many variations to make this work, and NOTHING seems to work. 🤷🏻‍♂️

Collapse
 
jpolstre profile image
jpolstre • Edited

Thanks, in my case work whitout: declare global {

Collapse
 
asgamboa12 profile image
Asgamboa12

Thank you very much, this problem was killing me and I could not find a way to solve it until I saw your post, thank you very much !!!

Collapse
 
alphakennybudy profile image
Adil • Edited

Thank you, it helped. But as far as I understood, it adds currentUser field to all Requests? What if I want it to be present only in the current file and not declared in the other files where I use Request?

Collapse
 
lucamartins profile image
Luca S. Martins

You could declare currentUser as optional and use it when needed

Collapse
 
jonn_joo profile image
Ioannis • Edited

Could you add a GitHub link please. It does not work for me as well.

Collapse
 
kwabenberko profile image
Kwabena Bio Berko
Collapse
 
pendar747 profile image
Pendar

This was awesome! I was missing the step with having to create the same folder path structure and adding the "typeRoots"

Collapse
 
sellonen profile image
Sakari Ellonen

I created an account here just to say thank you, this was such a nice solution that a typescript newbie like me would not have come up on my own.

Collapse
 
kwabenberko profile image
Kwabena Bio Berko

I'm really glad you found it useful.