typescriptexpressexpress-session

Extending session object in express-session


I know this is not an actual issue, but a help wanted.

I'm struggling with typescript and express session and I have been playing around and trying to fiugure out this one for quite a bit.

I'm trying to extend my session object, for that, I'm trying to do a merge of typings as per in the documentation:

I have a types/session.d.ts with the following interface to be merged:

declare module 'express-session' {
  interface SessionData {
    userId: string;
  }
}

But this is not working, as an example, in other-folder/some.ts

req.session.userId = user.id;
// Property 'userId' does not exist on type 'Session & Partial<SessionData>'.

However, if I import Session from express-session, it does work:

import { Session } from 'express-session'

declare module 'express-session' {
  interface SessionData {
    userId: string;
  }
}

I'm not very proficient with TypeScript and I'm unsure about importing a module in a type defintion, event TypeScript complains about this (warning):

'Session' is declared but its value is never read.

I'm wondering, is this the right way of tackling the problem?

What can I do different?

Kind regards!

PS: my tsconfig should be fine as I have available through my code other type definitions and they are working with no issues at all.


Solution

  • You should use Module Augmentation. You should also know this from Modules:

    In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).

    For example:

    ./src/main.ts:

    import express from 'express';
    import session from 'express-session';
    
    const app = express();
    
    app.use(
      session({
        secret: 'keyboard cat',
        resave: false,
        saveUninitialized: true,
        cookie: { secure: true },
      }),
    );
    app.get('/', (req, res) => {
      const user = { id: '1' };
      req.session.userId = user.id;
    });
    

    ./types/session.d.ts: Make sure you include at least one top-level import or export to make this file as a module, NOT a script whose contents are available in the global scope. Sometimes, you will import and use some interfaces or types from third-party node modules. But in your case, you don't need it. So just use export {} or import 'express-session', both of them are ok.

    declare module 'express-session' {
      interface SessionData {
        userId: string;
      }
    }
    
    export {};
    

    tsconfig.json:

    "typeRoots": [
      "./node_modules/@types",
      "./types",
    ], 
    

    package versions:

    "express": "^4.17.1",
    "@types/express": "^4.17.11",
    "typescript": "^3.9.7"
    "express-session": "^1.17.1",
    "@types/express-session": "^1.17.3",