【问题标题】:Proper use of TypeScript Generics正确使用 TypeScript 泛型
【发布时间】:2022-01-16 05:57:21
【问题描述】:

我的目标是按操作对请求和响应进行分组,所以如果我有(如下面的代码)两个操作:PlayStay,我将有一对代表请求的请求/响应/response of Play action 和 Stay action 相同。

我想表达一个约束只能耦合同一个动作的请求和响应(see Playground

type ActionName = "Play" | "Stay";

type RequestD<A extends ActionName, B extends Record<string, unknown>> = {
  _tag: "Request";
  _action: A;
} & B;

type ResponseD<A extends ActionName, B extends Record<string, unknown>> = {
  _tag: "Response";
  _action: A;
} & B;

type PlayRequest = RequestD<"Play", { a: number }>;
type PlayResponse = ResponseD<"Play", { b: number }>;

type StayRequest = RequestD<"Stay", { c: number }>;
type StayResponse = ResponseD<"Stay", { d: number }>;

type ActionOf<X> = X extends RequestD<infer A, any>
  ? A
  : X extends ResponseD<infer A, any>
  ? A
  : never;

// Problem:
// X is "Play" | "Stay"`
// Is there a way to get "Play"?
type X = ActionOf<PlayRequest>;

type TagOf<X> = X extends RequestD<any, any>
  ? "Request"
  : X extends ResponseD<any, any>
  ? "Response"
  : never;

// Problem:
// Y1 is "Request" and that's expected
// Y2 is "Request" as well and that's unexpected, how is this possible? How can I fix it?
type Y1 = TagOf<PlayRequest>;
type Y2 = TagOf<PlayResponse>;

type RoundTrip<
  A extends ActionName,
  Req extends RequestD<A, any>,
  Res extends ResponseD<A, any>
> = {
  _action: A;
  request: Req;
  response: Res;
};

type PlayRT = RoundTrip<"Play", PlayRequest, PlayResponse>;

// Problem:
// The following should be wrong because the first request is related to action "Stay", not "Play"
// Is there a way to enforce that?
type WrongRT = RoundTrip<"Play", StayRequest, PlayResponse>;

我在上面的代码中提出的问题是:

  1. 如何将“播放”操作关联到请求/响应以在类型级别使用它?
  2. 为什么即使这些类型被适当标记(_tag 字段),我也无法区分请求/响应?
  3. 如何将操作限制为RoundTrip 类型的请求/响应中的相同操作?

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    问题其实很简单,你在RequestResponse中使用any作为第二个参数。

    如果你看一下你的定义:

    type RequestD<A extends ActionName, B extends Record<string, unknown>> = {
      _tag: "Request";
      _action: A;
    } & B; // note that you are extending the entire object.
    

    然后当你这样定义一个泛型时:

    type TagOf<X> = X extends RequestD<any, any>
      ? "Request"
      : X extends ResponseD<any, any>
      ? "Response"
      : never;
    

    它将失败,因为any 包含一个对象,例如用于响应的{ _tag: "Request" } 或用于请求的{ _tag: "Response" }

    它基本上没有让打字稿工作。要解决此问题,您只需将any 更改为类似空对象{}

    Playground

    【讨论】:

    • 谢谢!这就是第二个问题的答案,其他问题有什么线索吗?
    • 等等,它也解决了第 3 个问题,只剩下第 1 个问题,有什么建议吗?
    • 没关系,您在所有帐户上都是对的,通过在ActionOf 中使用RequestD&lt;any, {}&gt;ResponseD&lt;any, {}&gt; 我得到了正确答案。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-25
    • 2013-04-29
    • 1970-01-01
    • 2019-07-23
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多