5
votes

I am trying to extend the Request object of express that is available on express.Request so that it includes some other data inside the session. I have tried creating my own typings file (d.ts) and using the following code:

import * as express from 'express';

declare module 'express' {
    export interface Request {
        session: express.Request['session'] & {
            myOwnData: string;
        }
    }
}

I am greeted with the following error:

'session' is referenced directly or indirectly in its own type annotation.

What is the correct way to implement this?

1
update the post for imported express - Aravind
why not create your own Request ? interface MyRequest extends Request { session: string } - dege
Because when I do that, my typescript program is still using the original interface and not the new one. I would have to manually import and use the other name everywhere unless there is another way I am not seeing - itsundefined
Try using declaration merging to directly extend the static type. Note I didn't find the d.ts declaring session (I am on mobile). Second, you could try to type alias before and use the aliased type as the static type for session. - Stefan Hanke
Session is being added by the express-session module. Declaration merging is only to add new types not extend them. If I wanted to add something else to the request interface I could do it. What I can't do is update the session that is inside Request. I also tried using a type alias to import Request but the program would still use the original Request interface and not the updated one. - itsundefined

1 Answers

4
votes

So looking at the express-session type declaration, it declares a Session (and modified Request object) under the Express namespace. Using this we can create a type declaration (mySession.dt.s) to augment the default properties, bypassing the limits of declaration merging:

import {Express} from 'express';
import 'express-session';

declare module 'express' {
    export interface Request {
        session: Express.Session & {
            myOwnData: string;
            myOwnData2: number;
            myOwnData3: boolean;
        };
    }
}

Note that it seems that the compiler is somewhat flexible about the imports in this file (like it doesn't seem to care if Express or Request is imported), but being explicit will be most consistent.

We can then import this declaration into our server file:

import express = require('express');
import {Express, Request, Response} from 'express';
import './mySession';
import * as session from 'express-session';

const app: Express = express();
const PORT = process.env.PORT || process.env.NODE_PORT || 3000;

app.use('/endpoint', (req: Request, res: Response) => {
   const a: number = req.session.myOwnData3 + 2; // fails to compile and highlighted by editors
   console.log(req.session.myOwnData); // compiles and provided autocomplete by Webstorm
   // do stuff
   return res.ok();
});

app.use(session(/** do session stuff **/));

log.info(`Start Express Server on ${PORT}`);
app.listen(PORT);

As tested, this structure achieves type-safety on the added properties, and intellisense/autocomplete in both VSCode and WebStorm with both the express-session and added properties.

Unfortunately, as you noted, this won't be applied globally where type inference is utilized, (only where you explicitly import Request). If you want full control over the interface, you could uninstall @types/express-session and just copy & modify the d.ts (and import that). Another possibility is to declare a totally new property and implement, but obviously a lot more work.