You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Express+TypeScript+Passport中让中间件后req.user必填?

如何在Express中间件验证后让req.user类型变为必填?

每次编写路由控制器时,都要重复写这段代码:

if (!req.user) return res.status(401).send()

我尝试用Express中间件拦截未登录用户的请求,但遇到了类型标注的问题:Express默认的req.user是可选字段,直接重写全局类型把user设为必填并不合理——毕竟只有经过中间件验证的路由才需要user必填。如果直接给控制器的req参数指定包含必填user的类型,路由会因为类型不匹配报错。请问该怎么解决这个问题?


上下文代码片段

全局Express类型重写

declare global {
  namespace Express {
    interface User extends TUser {
      _id: ObjectId
    }
  }
}

期望实现的效果

// 路由
router.post('/', sessionMiddleware, controller)

// 中间件
const sessionMiddleware = (req: Request, res: Response, next: NextFunction) => {
  if (!req.user) return res.status(401).send()
  next()
}

// 控制器
const controller = (req: RequestWithRequiredUser, res: Response) => {
  // 此处user的类型不可能为undefined
  const user = req.user
  // ...业务逻辑
}

当前实际操作

const controller = (req: Request, res: Response) => {
  if (!req.user) return res.status(401).send()

  // ...其他业务逻辑
}

解决方案

方案一:自定义带类型断言的中间件

先定义包含必填user的Request类型,再在中间件验证通过后通过类型断言转换req的类型:

// 定义带必填user的Request类型
type RequestWithRequiredUser = Express.Request & {
  user: Express.User;
};

// 认证中间件
const requireAuth = (req: Express.Request, res: Express.Response, next: Express.NextFunction) => {
  if (!req.user) {
    return res.status(401).send();
  }
  // 类型断言,告知TypeScript此时req.user已存在
  next(null, req as RequestWithRequiredUser);
};

// 路由使用示例
router.post('/', requireAuth, (req: RequestWithRequiredUser, res) => {
  // 此处req.user类型为必填,不会提示undefined
  console.log(req.user._id);
});

方案二:使用类型守卫函数

写一个类型守卫函数,在控制器里快速验证并缩小req的类型范围:

// 定义带必填user的Request类型
type RequestWithRequiredUser = Express.Request & {
  user: Express.User;
};

// 类型守卫函数
function hasRequiredUser(req: Express.Request): req is RequestWithRequiredUser {
  return req.user !== undefined;
}

// 控制器中使用
const controller = (req: Express.Request, res: Express.Response) => {
  if (!hasRequiredUser(req)) {
    return res.status(401).send();
  }
  // 此处req.user自动推断为必填类型
  console.log(req.user._id);
};

方案三:类型绑定的中间件(更优雅)

通过类型声明合并,让中间件后续的路由处理函数自动获得正确的类型:

// 定义带必填user的Request类型
type RequestWithRequiredUser = Express.Request & {
  user: Express.User;
};

// 给中间件添加类型,确保后续处理函数的req类型正确
const requireAuth: Express.RequestHandler = (req, res, next) => {
  if (!req.user) {
    return res.status(401).send();
  }
  next();
} as unknown as (
  req: Express.Request,
  res: Express.Response,
  next: (err?: any) => void
) => void;

// 路由使用时,控制器的req自动推断为RequestWithRequiredUser
router.post('/', requireAuth, (req, res) => {
  console.log(req.user._id); // 无类型提示问题
});

内容的提问来源于stack exchange,提问作者Luna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 23:55:31