![](/img/trans.png)
[英]Typescript - How to dynamically type an object with keys from a dynamic array of objects
[英]Typescript: Dynamic object with keys from values of an array
我已經制作了一個 class 來批量處理承諾並根據它們給出的鍵返回結果。例如,如果你給它兩個名為order
和customer
的鍵,每個鍵都有一個 promise,它將解決這些承諾並返回一個 ZA8CFDE6331BD59EB62AC9C64將這些鍵作為屬性,將解析的值作為它們各自的值。
所以這里是如何使用這個 class :
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 }>
我如何重構它以獲取 IDE 向我建議的order
或customer
?
這里的問題是BatchPromise
類型對它所持有的特定鍵和值一無所知。 如果您希望它跟蹤這一點,它需要是像BatchPromise<T>
這樣的泛型類型,其中T
是 object 類型,表示在resolveAll()
中返回的鍵值映射。
class BatchPromise<T extends object = {}> {
...
async resolveAll(): Promise<T> { ... }
}
因此,每次調用add()
時,您都會從BatchPromise<T>
更改為BatchPromise<T & Record<K, V>>
其中K
和V
分別是您的鍵和值類型。 這遇到了一個問題:類型系統不支持任意更改現有 object 的類型。 如果你很小心,你可以編寫BatchPromise
以便add()
被視為縮小類型,這是受支持的; 這將需要使用斷言 function (以便add
返回asserts this is BatchPromise<T & Record<K, V>>
)。 但是斷言函數現在不是很容易使用,(參見microsoft/TypeScript#33622 ),所以我不打算提供這樣的解決方案。
如果bp.add()
返回一個修改后的類型的BatchPromise
object ,則不是讓bp.add()
方法更改bp
的類型,而是在類型系統中變得更好:
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
具有customer
和order
屬性。
好的,希望能給你一個前進的方向。 祝你好運!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.