简体   繁体   English

正确使用 TypeScript Generics

[英]Proper use of TypeScript Generics

My goal here is to have requests and responses grouped by action, so if I have (as in the code below) two actions: Play and Stay , I will have a pair of request/response representing the request/response of Play action and the same for the Stay action.我的目标是按动作对请求和响应进行分组,因此如果我有(如下面的代码)两个动作: PlayStay ,我将有一对表示Play动作的请求/响应的请求/响应和Stay动作也是如此。

I want to express the constraint that only request and response of the same action can be coupled ( see Playground )我想表达只能耦合相同动作的请求和响应的约束( 参见 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>;

My questions stated in the code above are:我在上面的代码中提出的问题是:

  1. How can I associate the "Play" action to a request/response to use it at type level?如何将“播放”操作与请求/响应相关联以在类型级别使用它?
  2. Why I cannot differentiate between Request/Response even if those types are appropriately tagged ( _tag field)?为什么即使这些类型已适当标记( _tag字段),我也无法区分请求/响应?
  3. How can I constraint the action to be the same action in request/response in RoundTrip type?如何将操作限制为RoundTrip类型的请求/响应中的相同操作?

The issue is actually quite simple, you are using any as the second parameter in Request and Response .这个问题实际上很简单,您在RequestResponse中使用any作为第二个参数。

If you take a look at your definition:如果你看一下你的定义:

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

Then when you then define a generic as such:然后,当您这样定义泛型时:

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

It will fail because any includes an object such as { _tag: "Request" } for a response or { _tag: "Response" } for a request.它将失败,因为any包含 object 例如{ _tag: "Request" }用于响应或{ _tag: "Response" }用于请求。

It basically gives typescript nothing to work off of.它基本上使 typescript 没有什么可解决的。 To fix this you just need to change the any to something like an empty object {} .要解决此问题,您只需将any更改为类似空的 object {}

Playground操场

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM