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

如何用TypeScript编写异步Express中间件、定义返回值并扩展Request对象

解决Express中async/await中间件返回值与Request对象重载的问题

我来帮你拆解并解决这两个关联的问题:首先是你遇到的类型不兼容报错,然后是async/await中间件的返回值定义规范。

一、先搞定Request对象类型不匹配的报错

你当前的核心问题是RequestHandler默认要求req参数是Express原生的Request类型,但你传入了自定义的IUserRequest,导致TS提示类型不兼容。这里有两种更优雅的解决方式:

1. 全局扩展Express的Request接口(推荐)

这种方式不需要每次都手动声明IUserRequest,直接在项目的类型定义文件(比如src/types/express.d.ts)里扩展原生的Request接口:

// 引入你的IUserData接口
import { IUserData } from '../path-to-your-interface';

declare global {
  namespace Express {
    interface Request {
      user?: IUserData; // 用可选属性更合理,因为不是所有请求都会经过这个认证中间件
    }
  }
}

写完之后记得确保这个文件被TS识别(在tsconfig.json的include数组里添加这个路径)。这样所有地方的Request对象都会自动带上user属性,你的中间件就可以简化成:

import { NextFunction, Request, RequestHandler, Response } from 'express'
import { ValidationError } from './your-error-module';
import { decodeJwt } from './your-jwt-module';

const authenticate: RequestHandler = async (req, res, next) => {
  if (!req.headers.authorization) {
    return next(new ValidationError())
  }
  const payload = await decodeJwt(req.headers.authorization);
  req.user = payload;
  return next();
};

2. 用类型断言适配自定义Request类型

如果你不想全局扩展,也可以直接给中间件做类型断言,告诉TS这个自定义中间件符合RequestHandler的要求:

import { NextFunction, Request, RequestHandler, Response } from 'express'
import { ValidationError } from './your-error-module';
import { decodeJwt } from './your-jwt-module';

interface IUserRequest extends Request {
  user: IUserData;
}

const authenticate = async (req: IUserRequest, res: Response, next: NextFunction) => {
  if (!req.headers.authorization) {
    return next(new ValidationError())
  }
  const payload = await decodeJwt(req.headers.authorization);
  req.user = payload;
  return next();
} as RequestHandler; // 这里的类型断言是关键

二、async/await中间件的返回值定义

对于Express的async中间件,返回Promise<void>是完全合法且规范的,Express内部会自动处理async函数的Promise:

  • 当你调用next()时,Promise会在后续中间件执行完成后resolve;
  • 如果async函数抛出异常(比如await的Promise reject,或者主动throw new Error()),Express会自动把这个异常传递给错误处理中间件(就是那些带四个参数(err, req, res, next)的中间件)。

你也可以省略返回值类型标注,因为TS会自动推断async函数返回Promise类型:

const authenticate: RequestHandler = async (req, res, next) => {
  // ...你的认证逻辑
};

需要注意的是:不要在同一个async中间件里同时调用next()和发送响应(比如res.send()),这会导致Express报错,因为一个请求只能有一次响应输出。

最后总结一下

  1. 扩展Request对象优先用全局类型扩展的方式,整个项目都能复用,类型兼容性最好;
  2. async/await中间件返回Promise<void>是完全正确的写法,Express对这种类型的中间件支持很好;
  3. 避免在async中间件里同时调用next()和发送响应,防止重复响应的错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:28:47