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

Express与jsonwebtoken实现JWT签名时的运行时类型不兼容问题排查与修复

Express与jsonwebtoken实现JWT签名时的运行时类型不兼容问题排查与修复

我来帮你分析并解决这个问题,先理清楚你遇到的核心矛盾:代码在VSCode里没有类型报错,但运行时却抛出了类型不兼容的错误,本质是自定义请求类型和Express默认类型的匹配问题。

先贴一下你的代码方便参考:

import jwt from 'jsonwebtoken';
import { Request, Response } from 'express';
import { JWT_EXPIRY, JWT_SECRET } from '../../config';

interface UserParams {
  username: string,
  password: string
}

interface ReqWithUserParams extends Request {
  user: UserParams
}

const createAuthToken = (user: UserParams): string => {
  return jwt.sign({ user }, JWT_SECRET, {
    subject: user?.username,
    expiresIn: JWT_EXPIRY,
    algorithm: 'HS256'
  });
};

export const login = (req: ReqWithUserParams, res: Response) => {
  const authToken = req.user && createAuthToken(req.user);
  if (authToken) {
    res.json({ authToken });
  } else {
    res.status(401).json({ message: 'Unauthorized' });
  }
};

export const refresh = (req: ReqWithUserParams, res: Response) => {
  const authToken = req.user && createAuthToken(req.user);
  if (authToken) {
    res.json({ authToken });
  } else {
    res.status(401).json({ message: 'Unauthorized' });
  }
};

你遇到的运行时错误信息如下:

The last overload gave the following error.
Argument of type '(req: ReqWithUserParams, res: Response) => void' is not assignable to parameter of type 'RequestHandlerParams<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
Type '(req: ReqWithUserParams, res: Response) => void' is not assignable to type 'RequestHandler<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
Types of parameters 'req' and 'req' are incompatible.
Type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' is not assignable to type 'ReqWithUserParams'.
Types of property 'user' are incompatible.
Type 'User | undefined' is not assignable to type 'UserParams'.
Type 'undefined' is not assignable to type 'UserParams'.

为什么会出现这个问题?

VSCode里没报错是因为你直接给req指定了ReqWithUserParams类型,相当于在静态类型检查阶段告诉TS:这个req一定有符合UserParams的user属性。但实际运行时,Express传入的原生Request对象中,user属性本身是可能为undefined的(或者是Express默认的User类型,和你定义的UserParams不匹配)。

当你把login/refresh作为Express路由处理函数时,Express会传入它自己的Request对象,这个对象的user属性并不满足你自定义的UserParams类型要求,所以运行时就触发了类型不兼容的错误。简单说就是:你在TS里强行给req指定了更严格的类型,但实际传入的req并不符合,静态检查被“骗过”了,但运行时露馅了。

怎么修复?

有两种合理的修复方式,你可以根据项目场景选择:

方式一:调整自定义接口,兼容undefined

修改你的ReqWithUserParams接口,让user属性变成可选(允许为undefined),这样就能和Express默认的Request类型兼容:

// 修改后的接口定义
interface ReqWithUserParams extends Request {
  user?: UserParams  // 加问号标记为可选属性
}

你已经在处理函数里写了req.user的存在性判断,所以这个修改不会影响业务逻辑,同时能解决类型不兼容的问题。

方式二:全局扩展Express的Request类型(更推荐)

如果希望整个项目都能使用自定义的User类型,可以直接扩展Express的默认类型,不用每次都写自定义接口。

在项目中创建types/express/index.d.ts文件(路径可自行调整,确保TS能识别到),写入以下内容:

declare namespace Express {
  export interface Request {
    user?: import('../../path-to-your-user-params').UserParams;  // 替换为UserParams的实际文件路径
  }
}

之后在路由处理函数里直接使用原生Request类型即可,它会自动包含user?: UserParams的属性:

export const login = (req: Request, res: Response) => {
  const authToken = req.user && createAuthToken(req.user);
  if (authToken) {
    res.json({ authToken });
  } else {
    res.status(401).json({ message: 'Unauthorized' });
  }
};

另外提个小细节:createAuthToken函数的参数user是UserParams类型,其中username是必填项,所以user?.username里的问号可以去掉,避免不必要的可选链判断。

备注:内容来源于stack exchange,提问作者ossiggy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 11:23:05