typescript - 打字稿:具有来自数组值的键的动态对象
问题描述
我已经创建了一个类来批量处理承诺并根据给定的键返回结果。例如,如果你给它两个名为order
and的键customer
,每个键都有一个承诺,它将解析这些承诺并返回一个对象这些键作为属性,解析的值作为它们各自的值。
所以这里是如何使用这个类:
const batchPromiseHandler = new BatchPromise();
// getCustomerInfo and getPaymentInfo will give back a promise which resolves into their data
batchPromiseHandler.add('order', getOrderInfo());
batchPromiseHandler.add('customer', getCustomerInfo());
// await to resolve all into result object
const result = await batchPromiseHandler.resolveAll();
console.log(result.order); // <<-- I want to be able to get suggestion order or customer from IDE
console.log(result.customer);
这是实际的实现:
type resultDecorator = (data: any[], index: number) => any;
class BatchPromise {
private promiseList: Promise<any>[] = [];
private keyList: string[] = [];
private decoratorList: resultDecorator[] = [];
add(key: string, promise: Promise<any>, decorator?: resultDecorator): void {
if (this.keyList.indexOf(key) !== -1) {
throw new Error(`Key: "${key}" already exists in PromiseLand!`);
}
this.promiseList.push(promise);
this.keyList.push(key);
this.decoratorList.push(decorator);
}
async resolveAll(): Promise<{ [key: string]: any }> { // <<------ here is naive return type
const resolvedArray = await Promise.all(this.promiseList);
const result = {};
for (let index = 0; index < this.promiseList.length; index++) {
const key = this.keyList[index];
result[key] =
typeof this.decoratorList[index] === 'function'
? await this.decoratorList[index](resolvedArray[index], index)
: resolvedArray[index];
}
return result;
}
}
它按预期工作正常,但我希望能够自动完成resolveAll
函数的结果。我不知道如何使用语言的动态类型特性,所以我只是这样做了:
Promise<{ [key: string]: any }>
我如何重构它以便能够获得例如IDEorder
或customer
向我提出的建议?
解决方案
这里的问题是该类型BatchPromise
对它所持有的特定键和值一无所知。如果您希望它跟踪这一点,它需要是一个泛型类型,例如BatchPromise<T>
,其中T
表示返回的键值映射的对象类型resolveAll()
。
class BatchPromise<T extends object = {}> {
...
async resolveAll(): Promise<T> { ... }
}
因此,每次调用时add()
,您都会从 a 更改BatchPromise<T>
为BatchPromise<T & Record<K, V>>
where ,K
并且V
分别是您的键和值类型。这有点麻烦:类型系统不支持任意更改现有对象的类型。如果你很小心,你可以BatchPromise
这样写,这add()
被视为缩小了类型,这是受支持的;这将需要使用断言函数(以便add
返回asserts this is BatchPromise<T & Record<K, V>>
)。但是断言函数现在不是很容易使用,(参见microsoft/TypeScript#33622),所以我不打算提供这样的解决方案。
如果返回修改后类型的对象,则类型系统中的事情会变得更好,而不是让bp.add()
方法更改 的类型:bp
bp.add()
BatchPromise
add<K extends string, V>(
key: K, promise: Promise<V>, decorator?: resultDecorator
): BatchPromise<T & Record<K, V>> {
...
return this as BatchPromise<T & Record<K, V>>;
}
为了使它起作用,您需要更改调用add()
方式以合并方法链接而不是多个语句:
const batchPromiseHandler = new BatchPromise()
.add('order', getOrderInfo())
.add('customer', getCustomerInfo());
这样你batchPromiseHandler
的类型就会像BatchPromise<{order: OrderInfo, customer: CustomerInfo}>
.
让我们看看这是否有效:
const result = await batchPromiseHandler.resolveAll();
result.customer; // CustomerInfo
result.order; // OrderInfo
result.randomThing; // error!
// Property 'randomThing' does not exist on type
// 'Record<"order", OrderInfo> & Record<"customer", CustomerInfo>'
看起来不错。您可以验证(通过下面的 Playground 链接)IDE 的 IntelliSense 将能够提示result
具有 acustomer
和 aorder
属性。
好的,希望能给你一个前进的方向。祝你好运!
推荐阅读
- java - 在 JavaFX 时间轴完成后返回一个值
- python - Python:如果键不在字典中,为什么不调用 __eq__()?
- java - 如果对象已存在于带有 JPA 的数据库中,如何在保存操作中失败
- automation - calabash-android 无法拖动或滑动滑块按钮
- drag-and-drop - JqGrid - 拖放 - 更改页面重新加载网格
- mysql - 如何在 SQL 中检索具有特定数量的不同列值的行?
- angular - 时刻给我 Angular 5 html 模板中的错误
- javascript - 如何将特定对象发送到 Google Analytics 以显示在网站所有者的表格中?
- php - PHP四舍五入到小数点后2位
- html - CSS 表格中的中间单元格未正确居中