用泛型键索引对象



这是我第一次遇到这个问题,如果标题没有意义,我很抱歉,我想不出一个好的方式来表达。

我正在使用一个返回如下响应的API:

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

其中Customers可以替换为API可能返回的任何类型。所以它也可以像这样:

{
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可能返回的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

沙箱

最新更新