TypeScript: Extend Express.Session interface with own class
Asked Answered
I

5

28

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?

Interracial answered 11/8, 2016 at 15:28 Comment(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. – Chelyuskin
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. – Interracial
F
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
    }
  }
}
Fabyola answered 15/11, 2019 at 15:32 Comment(2)
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 – Interracial
This didn't work for me, but the other answer did. @Mr.Smith – Bootlace
C
60

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

Cumulus answered 13/1, 2021 at 5:59 Comment(3)
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"). – Incoordinate
very clear and concise with code snippets and .json versions. thank you. – Municipal
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 – Piton
F
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
    }
  }
}
Fabyola answered 15/11, 2019 at 15:32 Comment(2)
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 – Interracial
This didn't work for me, but the other answer did. @Mr.Smith – Bootlace
A
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

Acyl answered 11/1, 2022 at 14:28 Comment(0)
R
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
    }
}
Rubie answered 11/7, 2022 at 12:59 Comment(0)
J
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.

Jowl answered 20/3, 2023 at 11:5 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.