22

I'm working on a Typescript project with npm packages. I want to add a property to the Express.Session interface.

example Class:

class User {
    name: string;
    email: string;
    password: string;
}

export = User;

New d.ts file for the interface definition (don't want to edit express-session.d.ts):

declare namespace Express {
    interface Session {
        user: User
    }
}

app.ts

import User = require('./User');

function (req: express.Request, res: express.Response) {
    req.session.user //I want to use it like this.
}

the problem is, that User is not known in de d.ts file. But neither require nor import the User-file fixes that.

How can I add my own class to the session interface?

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Mr. Smith
  • 559
  • 1
  • 5
  • 17
  • You probably can't do that. You can declare to the typescript compiler that the `Session` instance has this user property, but in reality (at run time) that won't be the case. Express will execute your function and they will pass a request which has a session, but this session won't have this user. – Nitzan Tomer Aug 11 '16 at 15:31
  • it works if i also implement it as an interface in the d.ts file. But it would be better if I only have to write it once. Or at least class and interface in the same file. – Mr. Smith Aug 12 '16 at 08:12

6 Answers6

53

May be due to the package version, the answer @Vitalii Zurian provided does not work for me. If you want to extend session data on req.session and pass the TSC type checks, you should extend SessionData interface.

E.g.

User.ts:

class User {
  name: string = '';
  email: string = '';
  password: string = '';
}

export = User;

app.ts:

import express from 'express';
import User from './User';

declare module 'express-session' {
  interface SessionData {
    user: User;
  }
}

function controller(req: express.Request, res: express.Response) {
  req.session.user;
}

package versions:

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

result:

enter image description here

Lin Du
  • 88,126
  • 95
  • 281
  • 483
  • Thanks, this is the code that worked for me with recent versions of the packages ("express": "^4.17.1", "express-session": "^1.17.2", "typescript": "^3.6.4"). – workwise Jun 02 '21 at 10:28
  • very clear and concise with code snippets and .json versions. thank you. – java-addict301 Mar 21 '22 at 19:44
  • Can someone direct me to the correct documentation of the answer. I read this documentation but got lost halfway through https://www.typescriptlang.org/docs/handbook/declaration-merging.html – adir Mar 14 '23 at 09:55
8

A bit late to the party, but it should be possible to import your interface to the definitions file

import { User } from '../models/user';

declare global {
  namespace Express {
    interface Session {
      _user?: User
    }
  }
}
Vitalii Zurian
  • 17,858
  • 4
  • 64
  • 81
1

For me types.express-session.d.ts:

declare namespace Express {
    interface CustomSessionFields {
        myCustomField: string
    }

    export interface Request {
        session: Session & Partial<SessionData> & CustomSessionFields
    }
}

This answer relates with this post

MiF
  • 638
  • 7
  • 13
0

For those who want to set them in the index.d.ts as an ambient module declaration that will be merged with Express types this is what I have done:

declare namespace Express {
    interface CustomSessionFields {
        myCustomField: string;
    }
    type RequestExpress = import('express-serve-static-core').Request;
    type SessionExpress = import('express-session').Session;
    type SessionDataExpress = import('express-session').SessionData;
    export interface RequestExtended extends RequestExpress {
        session: SessionExpress & Partial<SessionDataExpress> & CustomSessionFields
    }
}
kimy82
  • 4,069
  • 1
  • 22
  • 25
0

I wonder, why does this not work:

export interface customSession extends SessionData {
  userId: string;
}

When I try to set the session:

req.session.userId = user.user_id;

I get the following error Property 'userId' does not exist on type 'Session & Partial<SessionData>'.

Mxngls
  • 437
  • 1
  • 5
  • 16
0

Even though I love Lin Du's answer, It might not work for usecases where you may have different Session type for different requests (one for user facing, one for admin panel, etc. -- In this case the session object is not constant)

I ended up creating CustomRequestHandlers to handle different type of Session types instead.

import session from 'express-session';
import { ParamsDictionary } from 'express-serve-static-core';
import { ParsedQs } from 'qs';
import { NextFunction, Request, RequestHandler, Response } from 'express';

interface CustomUserSession extends session.Session {
  user: {
    username: string;
  };
}

interface RequestWithSession<
  P = ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery = ParsedQs,
  LocalsObj extends Record<string, any> = Record<string, any>,
> extends Request<P, ResBody, ReqBody, ReqQuery, LocalsObj> {
  session: CustomUserSession;
}

export interface UserReqHandler<
  P = ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery = ParsedQs,
  LocalsObj extends Record<string, any> = Record<string, any>,
> extends RequestHandler {
  (req: RequestWithSession<P, ResBody, ReqBody, ReqQuery, LocalsObj>, res: Response<ResBody, LocalsObj>, next: NextFunction): void;
}

Once this was done I would just use it this way

export const handleUserAuth: UserReqHandler = async (req, res, next) => {
   req.session.user = {
      username: 'foo', // No Type errors.
   };
}

We can then create multiple Req Handlers based on our requirements.

Kush Daga
  • 96
  • 1