DEV Community

loading...

Extending Express' Types with TypeScript Declaration Merging - TypeScript 4

chris927 profile image Chris927 ・2 min read

TypeScript is evolving fast (as are a lot of tools in the Open Source space, gladly!)... but this means that what worked in a previous version may not work the same any more in the next major release. This happened to me in this case with TypeScript 4 and declaration merging.

There are good articles out there (like this one, thanks, Kwabena!), but it's slightly different in TypeScript 4 (and with modern typescript-eslint rules).

Sounds like your issue? Read on (or jump straight to the code example below).

To keep it simple, let's imagine we have some middleware (e.g. passport) that makes the current user available on each request, in the form of a userId (which may be of type string).

On some route or other middleware, we now want to access the userId like this:

app.get("/some-route", (req: Request, res: Response) => {
  res.send("Hello, world! Your userId is " + (req.userId || "not available"));
});
Enter fullscreen mode Exit fullscreen mode

TypeScript won't be happy with this, though. We will get an error like this:

Property 'userId' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs>'.ts(2339)
Enter fullscreen mode Exit fullscreen mode

We need to tell the Request interface that there is a userId property. But how?

The TypeScript 3 way of solving this (using declare global) may still work, but it would give me a warning, due to an eslint rule, which basically states that declare global is the old and outdated way.

The new way is to use declare module. In our example we can therefore introduce the userId to Express' Request type like this:

declare module "express-serve-static-core" {
  interface Request {
    userId?: string;
  }
}
Enter fullscreen mode Exit fullscreen mode

... and voila, the warning is gone, and (more importantly) type safety through TypeScript is restored.

(It isn't overly intuitive that the Request type must be extended in the module express-serve-static-core...)

Now, what if you add a field to the session (assuming you are using express-ession)? The additional field needs to be declared for the Session type, inside the express-session module, like this:

declare module "express-session" {
  interface Session {
    someSessionVar: string;
  }
}
Enter fullscreen mode Exit fullscreen mode

Discussion

pic
Editor guide
Collapse
voinik profile image
voinik

My god, you are a life saver! I've been looking for a way to do this. I came across the declare global approach, but that didn't work. I came across the export Request approach, but that broke the middleware I was using. This is the only approach I've seen that works. THANK YOU!

Collapse
chris927 profile image
Chris927 Author

Glad it helped! You're welcome.

Collapse
philipphock profile image
philipphock

This is very short and easily explained, but where do you save this. You can, of course, write it in every file you are using a session, but obviously that's not how you would do it. You probably want to use some sort of d.ts file. Could you explain how to use them properly.