打字稿:具有数组值键的动态对象

时间:2020-06-13 12:34:15

标签: typescript class

我制作了一个类来批量处理promise,并根据给出的键返回结果。例如,如果给它两个名为await Task.Runorder的键,每个键都有一个Promise,它将解决这些Promise,并以这些键作为属性,将解析的值作为它们各自的值,返回一个对象。

这是此类的用法:

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

如何重构它以使其能够获得IDE向我推荐的Promise<{ [key: string]: any }> order

1 个答案:

答案 0 :(得分:2)

这里的问题是类型BatchPromise对它所持有的特定键和值一无所知。如果要跟踪它,它必须是generic类型,例如BatchPromise<T>,其中T是表示{{ 1}}。

resolveAll()

因此,每次调用class BatchPromise<T extends object = {}> { ... async resolveAll(): Promise<T> { ... } } 时,您将从add()变为BatchPromise<T>,其中BatchPromise<T & Record<K, V>>K是您的关键和价值类型。这有点麻烦:类型系统不支持任意更改现有对象的类型。如果您小心一点,可以编写V,以便将BatchPromise视为缩小的类型,该类型受 支持。这将需要使用assertion function(以便add()返回add)。但是断言函数现在还不太容易使用(请参阅microsoft/TypeScript#33622),因此我将不提供这种解决方案。

如果asserts this is BatchPromise<T & Record<K, V>>返回修改后类型的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>>; } 的方式以合并method chaining而不是多个语句:

add()

这样,您的const batchPromiseHandler = new BatchPromise() .add('order', getOrderInfo()) .add('customer', getCustomerInfo()); 的类型将为batchPromiseHandler


让我们看看是否可行

BatchPromise<{order: OrderInfo, customer: CustomerInfo}>

看起来不错。而且,您可以(通过下面的Playground链接)验证IDE的IntelliSense能够提示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>' 具有resultcustomer属性。


好的,希望能给您前进的道路。祝好运!

Playground link to code