簡體   English   中英

Typescript:動態 object,鍵來自數組的值

[英]Typescript: Dynamic object with keys from values of an array

我已經制作了一個 class 來批量處理承諾並根據它們給出的鍵返回結果。例如,如果你給它兩個名為ordercustomer的鍵,每個鍵都有一個 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 向我建議的ordercustomer

這里的問題是BatchPromise類型對它所持有的特定鍵和值一無所知。 如果您希望它跟蹤這一點,它需要是像BatchPromise<T>這樣的泛型類型,其中T是 object 類型,表示在resolveAll()中返回的鍵值映射。

class BatchPromise<T extends object = {}> { 
  ... 
  async resolveAll(): Promise<T> { ... }
}

因此,每次調用add()時,您都會從BatchPromise<T>更改為BatchPromise<T & Record<K, V>>其中KV分別是您的鍵和值類型。 這遇到了一個問題:類型系統不支持任意更改現有 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具有customerorder屬性。


好的,希望能給你一個前進的方向。 祝你好運!

Playground 代碼鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM