NodeJS 在Express.js的扩展属性中使用Request的泛型参数

xzabzqsa  于 2023-08-04  发布在  Node.js
关注(0)|答案(2)|浏览(107)

我试图扩展Request类型以添加一个依赖于请求主体的属性,但是catchAsync中的泛型没有被正确应用,并且它总是any而不是传递的泛型参数
index.d.ts

import {RequestHandler} from 'express';
import {ParamsDictionary} from 'express-serve-static-core';
import {ParsedQs} from 'qs';

export interface CustomFormData<T> {
  data: T;
  fileNames: string[];
  buffers: Buffer[];
}

declare global {
  namespace Express {
    export interface Request<
      P = ParamsDictionary,
      ResBody = any,
      ReqBody = any,
      ReqQuery = ParsedQs,
      LocalsObj extends Record<string, any> = Record<string, any>,
    > {
      formData?: CustomFormData<ReqBody>;
    }
  }
}

字符串
middleware.ts

import {Request, Response, NextFunction} from 'express';

export type AsyncRequestHandler<M> = (
  req: Request<any, any, M, qs.ParsedQs, Record<string, any>>,
  res: Response,
  next: NextFunction,
) => Promise<void>;

export const catchAsync =
  <M = any>(fn: AsyncRequestHandler<M>): AsyncRequestHandler<M>=>
  async (req, res, next) => {
    await fn(req, res, next);
    if (res.headersSent) return;
    next();
  };


controller.ts

postProduct = catchAsync<IPostProduct>(async (req, res) => {
    const {data} = req.formData; // data is taken as "any" instead of "IPostProduct"

    await ProductController._productService.create(data);

    res.status(201).send();
  });

tkqqtvp1

tkqqtvp11#

这是因为当你用泛型声明AsyncRequestHandler类型,然后将req参数输入为Request<any, any, M, qs.ParsedQs, Record<string, any>>时,它会从声明合并中获取默认的any类型。有个办法

declare global {
  namespace Express {
    export interface Request<
      P = ParamsDictionary,
      ResBody = any,
      ReqBody = any,
      ReqQuery = ParsedQs,
      LocalsObj extends Record<string, any> = Record<string, any>,
    > {
      // Change type from `any` to `unknown` for type narrowing
      formData?: CustomFormData<unknown>
    }
  }
}

export type AsyncRequestHandler<M> = (
  // Add a constraint for `formData` to keep it from taking 
  // the static type determined by the declaration merging
  req: Request<any, any, M, qs.ParsedQs, Record<string, any>> & {formData?: CustomFormData<M>},
  res: Response,
  next: NextFunction,
) => Promise<void>;

export const catchAsync =
  <M>(fn: AsyncRequestHandler<M>): AsyncRequestHandler<M> =>
  async (req, res, next) => {
    await fn(req, res, next);
    if (res.headersSent) return;
    next();
  };

字符串
如果采用这种方法,您实际上不需要声明合并,除非您希望formData在所有地方都可用,请注意,在其他地方,如果不使用中间件,它仍然是any类型。

4urapxun

4urapxun2#

我想出了一个稍微不优雅的解决方案,替换了AsyncRequestHandler中的formData prop:

export type AsyncRequestHandler<M> = (
  req: Omit<Request<any, any, M, qs.ParsedQs, Record<string, any>>, 'formData'> & {
    formData?: CustomFormData<M>;
  },
  res: Response,
  next: NextFunction,
) => Promise<void>;

字符串

相关问题