TypeScript: 使用自定义类扩展 Express.Session 接口

25

我正在使用npm包进行TypeScript项目开发。我想要给Express.Session接口添加一个属性。

示例类:

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

export = User;

新增一个用于接口定义的d.ts文件(不想编辑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.
}

问题在于,在d.ts文件中未知用户。 但是,既不需要require也不需要导入User文件来解决此问题。

我该如何将自己的类添加到会话接口中?


你可能做不到那样。你可以向 TypeScript 编译器声明 Session 实例具有此用户属性,但实际上(在运行时)并非如此。Express 将执行您的函数,并传递一个带有会话的请求,但该会话将不具有此用户。 - Nitzan Tomer
如果我在d.ts文件中将其实现为接口,则它可以工作。但是如果我只需编写一次,那就更好了。或者至少在同一个文件中使用类和接口。 - Mr. Smith
6个回答

58

因为软件包版本的原因,@Vitalii Zurian提供的答案对我无效。如果您想要在req.session上扩展会话数据并通过TSC类型检查,您应该扩展SessionData接口。

例如:

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

包版本:

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

结果:

输入图像描述


谢谢,这是适用于最新版本的包(“express”:"^4.17.1",“express-session”:“^1.17.2”,“typescript”:“^3.6.4”)的代码。 - workwise
非常清晰简洁,附带代码片段和 .json 版本。谢谢。 - java-addict301
有人能指导我正确的答案文档吗? 我读了这个文档,但在一半的时候迷失了方向 https://www.typescriptlang.org/docs/handbook/declaration-merging.html - adir

9
有点晚了,但是将您的接口导入定义文件应该是可行的。
import { User } from '../models/user';

declare global {
  namespace Express {
    interface Session {
      _user?: User
    }
  }
}

有点晚了……这个问题是在我刚开始学习 TypeScript 的时候问的,现在已经很清楚了,但你的答案是正确的,所以我接受它。 - Mr. Smith
1
这对我没有用,但另一个答案有效。@Mr.Smith - giraffesyo

1

对于我来说,types.express-session.d.ts是什么:

declare namespace Express {
    interface CustomSessionFields {
        myCustomField: string
    }

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

这个答案与这个帖子有关。


0
我想知道,为什么这个不起作用:
export interface customSession extends SessionData {
  userId: string;
}

当我尝试设置会话时:

req.session.userId = user.user_id;

我遇到了以下错误:在类型“Session&Partial<SessionData>”上不存在属性“userId”。


0
尽管我喜欢Lin Du的答案,但它可能不适用于您可能有不同会话类型的用例(一个用于用户界面,一个用于管理面板等--在这种情况下,会话对象不是常量)。
最终,我创建了CustomRequestHandlers来处理不同类型的会话类型。
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;
}

完成这个后,我只需要这样使用它

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

我们可以根据需求创建多个Req处理程序。

0

对于那些想要将它们设置为与Express类型合并的环境模块声明的人,这是我所做的:

index.d.ts
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
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接