无法在 TypeScript 中扩展 Express 请求
Posted
技术标签:
【中文标题】无法在 TypeScript 中扩展 Express 请求【英文标题】:Unable to extend Express Request in TypeScript 【发布时间】:2019-05-30 12:29:11 【问题描述】:这个问题已经被问了好几次了,但是给出的答案都没有对我有用。我正在尝试扩展 Express Request
对象以包含一个用于存储 User
对象的属性。我创建了一个声明文件express.d.ts
,并将其放在与我的tsconfig.json
相同的目录中:
import User from "./src/models/user";
declare namespace Express
export interface Request
user: User;
然后我尝试在secured-api.ts
中对其进行分配:
import express from 'express';
import userService from '../services/user';
router.use(async (req, res, next) =>
try
const user = await userService.findByUsername(payload.username);
// do stuff to user...
req.user = user;
next();
catch(err)
// handle error
);
我收到以下错误:
src/routes/secured-api.ts:38:21 - error TS2339: Property 'user' does not exist on type 'Request'.
38 req.user = user;
~~~~
我的User
班级是:
import Model, RelationMappings from 'objection';
export class User extends Model
public static tableName = 'User';
public static idColumn = 'username';
public static jsonSchema =
type: 'object',
required: ['fname', 'lname', 'username', 'email', 'password'],
properties:
fname: type: 'string', minLength: 1, maxLength: 30 ,
lname: type: 'string', minLength: 1, maxLength: 30 ,
username: type: 'string', minLength: 1, maxLength: 20 ,
email: type: 'string', minLength: 1, maxLength: 320 ,
password: type: 'string', minLength: 1, maxLength: 128 ,
;
public static modelPaths = [__dirname];
public static relationMappings: RelationMappings =
;
public fname!: string;
public lname!: string;
public username!: string;
public email!: string;
public password!: string;
我的tsconfig.json
是:
"compilerOptions":
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es2015"], /* Specify library files to be included in the compilation. */
"outDir": "./build", /* Redirect output structure to the directory. */
"strict": true, /* Enable all strict type-checking options. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
我的目录结构是:
backend/
package.json
tsconfig.json
express.d.ts
src/
models/
user.ts
routes/
secured-api.ts
我在这里做错了什么?
【问题讨论】:
【参考方案1】:问题在于您没有扩充由express
定义的Express
全局命名空间,而是在您的模块中创建了一个新的命名空间(一旦您使用import
,该文件就会成为一个模块)。
解决办法是在global
中声明命名空间
import User from "./src/models/user";
declare global
namespace Express
export interface Request
user: User;
或者不使用模块导入语法,只引用类型:
declare namespace Express
export interface Request
user: import("./src/models/user").User;
【讨论】:
非常感谢!我将declare global
块放在express.d.ts
中,我必须从declare namespace Express
中删除declare
才能让它工作,但它现在可以工作了。
@user3814613 wops 忘记了其中的额外声明,将其删除。对不起
好心的先生。有什么办法可以用 tsc 自动完成吗?也许当我的src
文件夹中有express_ext.d.ts
时,express_ext.d.ts
的内容会自动在index.d.ts
中进行全局声明?
namespace Express
似乎对我不起作用。我不得不改用declare module 'express'
。知道为什么会这样吗?
declare namespace Express
解决方案在我在express/
目录中创建index.d.ts
文件后起作用。【参考方案2】:
我也遇到了同样的问题...
您不能只使用扩展请求吗? 喜欢:
interface RequestWithUser extends Request
user?: User;
router.post('something',(req: RequestWithUser, res: Response, next)=>
const user = req.user;
...
另一方面,如果您将异步回调与 express 一起使用,请确保使用 express-async 包装器或确保您确切知道自己在做什么。我推荐:awaitjs
【讨论】:
如果user
不是可选的,则不确定是否可以与strictFunctionTypes
一起使用,因为回调期望req
参数的键数比post
说它可以提供的要多
是的,这也可以,但另一个答案有一个更容易实施的解决方案。 @TitianCernicova-Dragomir 我只能通过将 user
设为可选来使其工作。如果它不是可选的,编译器会抱怨该函数不能分配给类型PathParams
。
我目前使用此解决方案将用户注入请求以进行身份验证和访问控制。 (用户被注入护照中间件)。用户需要是可选的......所以它并不完美。我不喜欢扩展另一个命名空间的想法,所以......我认为这是一个偏好问题。
为什么 user
字段在 RequestWithUser 中是可选的?我注意到如果它不是可选的,这将不起作用
我同意。至少对我来说,它看起来最干净。【参考方案3】:
0
声明合并意味着编译器将两个独立的同名声明合并到一个定义中。
见下面的例子,其中接口声明被合并到打字稿中
interface Boy
height: number;
weight: number;
interface Boy
mark: number;
let boy: Boy = height: 6 , weight: 50, mark: 50;
extend request response
【讨论】:
【参考方案4】:我尝试了很多解决方案,但都没有奏效。
但它也可以如此简单并且完美运行。 当然,您可以将 AuthenticatedRequest 保存在其他任何地方。
import IUser from './src/_models/user.model';
export interface AuthenticatedRequest extends Request
user: IUser;
static async getTicket(req: AuthenticatedRequest, res: Response)
更新:
所以我从这里发现了一些有趣的东西: Import class in definition file (*d.ts).
声明命名空间 Express 应该是 typescript 的第一行,将其计为全局
declare namespace Express
interface Request
user: import('./src/modules/models/user.model').IUser;
uploadedImage: key: string; type: string ;
group: import('./src/modules/models/group.model').IGroup;
【讨论】:
以上是关于无法在 TypeScript 中扩展 Express 请求的主要内容,如果未能解决你的问题,请参考以下文章
无法在不提供文件扩展名的情况下导入 TypeScript 模块
在 React 和 TypeScript 中扩展 HTML 元素,同时保留 props
在 ES6 模块中使用 typescript 支持扩展全局对象,如 Autodesk.Viewing.Extension