使用通用键索引 object

Indexing object with a generic key

我第一次 运行 进入这个问题,如果标题没有意义,我很抱歉,无法找到一个好的措辞。

我正在处理一个 API,return 的响应如下:

{
  PaginatedResults: {
    TotalResults:  100,
    Offset: 0,
    Limit: 0,
    PageSize: 100,
  },
  Error: {
    Message: '',
    Code: '',
  },
  Customers: [{...}]
}

其中 Customers 可以替换为 API 可以 return 的任何类型。所以它也可能看起来像这样:

{
  PaginatedResults: {
    TotalResults:  100,
    Offset: 0,
    Limit: 0,
    PageSize: 100,
  },
  Error: {
    Message: '',
    Code: '',
  },
  Sales: [{...}]
}

我目前表达的类型是这样的:

type PaginatedResponse<TResponse extends object> = 
  TResponse & 
   Readonly<{
    PaginatedResponse: {
      TotalResults:  number;
      Offset: number;
      Limit: number;
      PageSize: number;
    };
    Error?: {
      Message: string;
      Code: string;
    };
  }>;

这一直运行良好,但我最近 运行 在尝试创建通用辅助函数以自动对端点分页时遇到问题。

要跟踪所有结果,我需要执行以下操作:

const allResults = [...initialResults];
const nextResp = await apiCall();
allResults.push(...nextResp['Customers'])

但问题是关键 'Customers' 可以是 API 可能 return 的 50 多种类型中的任何一种。所以我需要一种通用的方法来索引响应。我尝试了很多不同的方法但都没有成功,所以我希望获得一些关于处理此类问题的最佳方法的指导。

单独使用 TypeScript 在这里用处不大。在 ...nextResponse['Customers'] 中,您希望 TypeScript 填写正确的密钥。但是正确的密钥是什么只能在运行时或通过手动提供的泛型类型获知。

但这也许对你有帮助:

// I added some generic types here which will help later
type PaginatedResponse<TResponse extends {[key in TKey]: any}, TKey extends keyof TResponse = keyof TResponse> = 
   Readonly<{
    PaginatedResponse: {
      TotalResults:  number,
      Offset: number,
      Limit: number,
      PageSize: number,
    };
    Error?: {
      Message: string;
      Code: string;
    };
  }> & TResponse

定义可能的 Response 类型:

type Response1 = PaginatedResponse<{Customers: any}>
type Response2 = PaginatedResponse<{Sales: any}>
type AllResponses = Response1 | Response2

现在让我们定义一个获取 PaginatedResponse 并将 TResponse 压入数组的函数:

type GetResponseKey<T> =  T extends PaginatedResponse<infer TRes, infer TKey> ? TKey : never

const arr: any[] = []

function pushToResponseArray<
  T extends AllResponses, 
  K extends GetResponseKey<T>
>(response: T, key: K){
  arr.push(response[key])
}

const res: Response1 = {} as any

pushToResponseArray(res, "Customers") // We have to manually specify the key here
// but typescript will check if key is correct

这不是一个完美的解决方案,我认为您总是必须手动设置 key 或使用一些 JavaScript 逻辑来确定它。

总结一下:

在您的应用程序中的某个地方,您需要一些 JavaScript 逻辑来确定 PaginatedResponse 的类型。之后将类型映射到相应的键以访问 TResponse.

Sandbox