31

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?

2
  • 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. Commented Aug 11, 2016 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. Commented Aug 12, 2016 at 8:12

5 Answers 5

67

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

Sign up to request clarification or add additional context in comments.

3 Comments

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").
very clear and concise with code snippets and .json versions. thank you.
Can someone direct me to the correct documentation of the answer. I read this documentation but got lost halfway through typescriptlang.org/docs/handbook/declaration-merging.html
11

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
    }
  }
}

2 Comments

just a little late... this question was in my first typescript days and quite clear now 😅 but your answer is correct so I accept it
This didn't work for me, but the other answer did. @Mr.Smith
2

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

Comments

1

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.

Comments

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
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.