简体   繁体   English

Typescript 是否可以根据参数枚举推断类型?

[英]Typescript is it possible to infer types based on argument enum?

Let's say that I have a function that has 3 arguments. The first argument is an enum that is defined by me, the second argument is an object that has the shape based on the value passed by the enum.假设我有一个 function,它有 3 个 arguments。第一个参数是我定义的枚举,第二个参数是 object,其形状基于枚举传递的值。 The third argument is just a string.第三个参数只是一个字符串。

Look at this function:看看这个 function:

  async callApi<Response = any, Input = any>(
    op: ApiOperations,
    input: Input,
    functionName: string
  ): Promise<Response> {
    return this.lambda.invoke<Response>(functionName, {
      op,
      input
    });
  }

You can see that I'm declaring this function as generic and receiving two types and setting the default to any and that works.您可以看到我将此 function 声明为通用类型并接收两种类型并将默认值设置为 any 并且有效。 But in this case whenever I want to call this function I have to manually specify the input and response types.但是在这种情况下,每当我想调用这个 function 时,我都必须手动指定输入和响应类型。 The thing is, I know that for each value from my ApiOperations enum, I have just one input type and one response type.问题是,我知道对于我的ApiOperations枚举中的每个值,我只有一种输入类型和一种响应类型。

So my question is, is there any way of typescript can infer the types based on the enum value?所以我的问题是,typescript 有什么方法可以根据枚举值推断类型吗?

An example of calling this function is:调用此 function 的示例是:

  async getChatRooms({ numberResults, siteId, startIndex }: GetChatRoomsInput): Promise<ChatRoom[]> {
    return this.api.callApi<ChatRoom[], GetChatRoomsInput>(ApiOperations.GetChatRooms, {
      siteId,
      numberResults,
      startIndex
    }, 'getChatRooms');
  }

This works fine, but the way that I want to do is:这很好用,但我想做的是:

  async getChatRooms({ numberResults, siteId, startIndex }: GetChatRoomsInput): Promise<ChatRoom[]> {
    return this.api.callApi(ApiOperations.GetChatRooms, {
      siteId,
      numberResults,
      startIndex
    }, 'getChatRooms');
  }

And for this case typescript would be able to tell me that the input has the GetChatRoomsInput type and ChatRoom[] as the response type.对于这种情况,typescript 将能够告诉我输入具有GetChatRoomsInput类型和ChatRoom[]作为响应类型。

You just just need a lookup type that maps enum values to input/response types.您只需要一个将枚举值映射到输入/响应类型的查找类型。 For example:例如:

enum ApiOperations {
  A,
  B,
}

interface ApiOperationsLookup {
  [ApiOperations.A]: {
    input: { a: number }
    response: { a: string }
  }

  [ApiOperations.B]: {
    input: { b: number }
    response: { b: string }
  }
}

Here ApiOperationsLookup is a type that has the key names of the ApiOperations mapped to specific input and response types.这里的ApiOperationsLookup是一种类型,它将ApiOperations的键名称映射到特定的输入和响应类型。

You can fetch the input type with something like:您可以使用以下内容获取输入类型:

type Test = ApiOperationsLookup[ApiOperations.A]['input']
// { a: number }

Now you can make callApi look like this:现在你可以让callApi看起来像这样:

  async callApi<T extends ApiOperations>(
    op: T,
    input: ApiOperationsLookup[T]['input'],
    functionName: string
  ): Promise<ApiOperationsLookup[T]['response']> {
    //...
  }

Here the generic parameter T is a value from the ApiOperations , and then input and the return type are pulled from the lookup map for that operation.这里通用参数T是来自ApiOperations的值,然后从该操作的查找 map 中提取input和返回类型。

This now works as I think you expect:这现在可以按我认为的那样工作:

const aResponse = await instance.callApi(ApiOperations.A, { a: 123 }, 'aFuncName')
// { a: string }

const aBadResponse = await instance.callApi(ApiOperations.A, { b: 123 }, 'aFuncName')
// type error on second argument

const bResponse = await instance.callApi(ApiOperations.B, { b: 123 }, 'aFuncName')
// { b: string }

Playground 操场


Another option is to skip the lookup type and instead use overloads where you create a function signature for each member of the enum:另一种选择是跳过查找类型,而是使用重载,为每个枚举成员创建一个 function 签名:

  // signature for each enum member
  async callApi(op: ApiOperations.A, input: { a: number }, functionName: string): Promise<{ a: string }>
  async callApi(op: ApiOperations.B, input: { b: number }, functionName: string): Promise<{ b: string }>

  // implementation
  async callApi(
    op: ApiOperations,
    input: unknown,
    functionName: string
  ): Promise<unknown> {
    //...
  }

I think that's a big uglier and harder to maintain personally, but that's a matter of opinion.我个人认为这是一个非常丑陋且难以维护的问题,但这是一个见仁见智的问题。

Playground 操场

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

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