函数包装器静态检查 HTTP 响应的参数

Posted

技术标签:

【中文标题】函数包装器静态检查 HTTP 响应的参数【英文标题】:Function wrapper statically checking parameters for HTTP response 【发布时间】:2021-12-25 15:04:24 【问题描述】:

我正在尝试为调用 Express'es 链 res.status(..).json(..) 的函数实现类型断言包装器。我想我“已经在那里”,但我卡住了。目标是静态检查是否:

传递的 HTTP 状态代码符合特定的响应负载 响应载荷具有一定的价值

TypeScript Playground 演示(完整实现尝试): click

其中一个错误来自我在上面链接的 TS 演示代码 [x] 中收到的列表:

类型“THTTPStatusCodeToData”上不存在属性“200”

包装器实现是:

function responseWrapper<
  DataKey extends keyof IEmbracedResponse
>(
  res: Res,
  status: keyof typeof mappedHTTPStatusCode,
  data: Record<
    DataKey, 
    THTTPStatusCodeToData[typeof status] // <-- so far, problem is here [x]
  >
) 
  return res.status(status).json(data); // just a chain call, but TS asserts correct `data` type for certain `status`

使用示例:

router.get('/', (req: Request, res: Response) => 
  if (!req.body.login) 
     return responseWrapper(res, 400,  error: 'Request payload lacks of "login"!' );
  

  return responseWrapper(res, 200,  payload:  isAdmin: true );
)

示例输入和 TS 预期类型检查结果:

responseWrapper(res, 200,  exception: Error('ups') ); // <-- fail, because 'exception' key cannot be associated with 200 status
responseWrapper(res, 500,  exception: 'something crashed'); // <-- fail, because 'exception' cannot be a string, but Error object
responseWrapper(res, 200,  something: null); // <-- fail, because status 200 cannot be associated with 'something' key
responseWrapper(res, 500,  error: 'ups' ); // <-- fail, because status 500 must be associated with 'exception' key, not the 'error' key

responseWrapper(res, 200,  payload:  isAdmin: true ); // <-- ok, because status 200 can be associated with 'payload' key and the payload has object value
responseWrapper(res, 500,  exception: Error('ups') ); // <-- ok, because status 500 can be associated with 'exception' key and the value is Error object
responseWrapper(res, 400,  error: 'ups' ); // <-- ok, because status 400 can be associated with 'error' key and it's a string

到目前为止,我使用了不太高级的包装器,只是为了检查传递的对象值是否与键正确关联,但现在我还希望获得关于值关联检查的 HTTP 状态:

const embraceResponse = <
  Key extends keyof IEmbracedResponse
>(response: Record<Key, IEmbracedResponse[Key]>) =>
  response;

// usage examples:
res.status(200).json(embraceResponse( payload:  prop: true  ));
res.status(400).json(embraceResponse( error: 'ups' ));

【问题讨论】:

【参考方案1】:

感谢完全正常工作的游乐场 :),我对您的代码进行了一些更改,关键更改是一种将状态转换为数据的实体类型。 responseWrapper 定义变得更简单。而且,您提供的支票现在似乎可以正常工作了。

type StatusToData<status> = 
  status extends keyof TypeOfHTTPStatusCodes['SUCCESSFUL'] ? Pick<IEmbracedResponse,'payload'> | Pick<IEmbracedResponse,'message'>
  : status extends keyof TypeOfHTTPStatusCodes['CLIENT_ERROR'] ? Pick<IEmbracedResponse, 'error'> 
  : status extends keyof TypeOfHTTPStatusCodes['SERVER_ERROR'] ? Pick<IEmbracedResponse, 'exception'>
  : never;

function responseWrapper<
   Status extends keyof typeof mappedHTTPStatusCode
>(
  res: Res,
  status: Status ,
  data: StatusToData<Status>,
) 
  return res.status(status).json(data); 

Full Code in playground

【讨论】:

我最终搞砸了,但它似乎奏效了。 github.com/ScriptyChris/Fake-PEV-Shopping/blob/…

以上是关于函数包装器静态检查 HTTP 响应的参数的主要内容,如果未能解决你的问题,请参考以下文章

包装在静态函数中时,Alamofire 4.0“调用中的额外参数'方法'”

从装饰器传递位置参数时如何支持静态和类方法?

使用可变参数模板函数围绕类实现基于 pImpl 的包装器

如何使泛型代理类型与静态类型检查器一起使用

用于 C++ 的 C# 包装器,但仅编译为静态库

将 youtube 视频包装在静态图像“框架”中并保持响应式调整大小