網站搜尋

泛型 - Typescript - 當函數中傳入多個鍵值時收窄 T[K] 類型


我正在嘗試編寫一個 hook,它接受一個對象的多個鍵,並根據當前應用的過濾條件篩選數據。所以,大致如下所示:

interface IFilterable<T,K extends keyof T & string>{
  key: K;
  customFilter?: (item: T[K], applied: T[K][]) => boolean
  //The hook uses some default logic to filter if this function isn't available.
}

function useFilters<T,K extends keyof T & string>(data: T[], filters: IFilterable<T,K>[]): T[]{
 //filter out the data, return new dataset.
}

問題在於,當傳入多個過濾器(IFilterable)時,customFilter 函數中的 item 和 applied 參數會變成鍵類型的聯合。或者,用程式碼表達就是...

useFilters(
  [{a: 1, b: "bob"], {a: 2, b: "berry"}],
  [{key: "a", customFilter: (item, applied) => {
    //Wanted: item has the type number
    //Reality: item has the type (string | number)
  }, 
  {key: "b", customFilter: (item, applied) => {
    //Wanted: item has the type string.
    //Reality: item has the type (string | number)
  }
}]);

我已經透過將所有東西都輸入為 unknown(或 unknown[]),然後再創建封裝函數將它們轉回適當類型,來“解決”這個問題,但如果可能的話,我想整個過程都保持強類型。

那麼,在這種情況下,有沒有任何方法,當我傳入一個物件陣列,其中一個屬性是鍵,另一個屬性是一個接受 T[K] 的函數時,能夠將類型縮小到特定的 T[K] 類型,而不是聯合類型?

謝謝。

答案#1

更新 2021年9月17日

我做了我的作業。

考慮這個例子:

/**
 * Obtain a union of all values of a dictionary
 */
type Values<T> = T[keyof T]

type FilterBuilder<T extends any[]> = Values<{
  [Key in keyof T[number]]: {
    key: Key,
    customFilter: (item: T[number][Key], applied: T[number][Key][]) => void
  }
}>

{
  //   type Test = {
  //     key: "a";
  //     customFilter: (item: 1, applied: 1[]) => void;
  // } | {
  //     key: "b";
  //     customFilter: (item: "str", applied: "str"[]) => void;
  // }
  type Test = FilterBuilder<[{ a: 1, b: 'str' }]>
}

const withFilters = <
  Item extends Record<PropertyKey, unknown>,
  Items extends Item[],
  >(data: [...Items]) => <
    Filter extends FilterBuilder<Items>,
    Filters extends Filter[],
    >(filters: [...Filters]) => { }

const useFilters = withFilters([{ a: 1, b: "bob" }, { a: 2, b: "berry" }])

const result = useFilters(
  [{
    key: "a", customFilter: (item, applied) => { }
  },
  {
    key: "b", customFilter: (item, applied) => { }
  }]
);

對 TypeScript 來說,很難推斷物件的 key 並立即將此類型應用到同一物件的另一個屬性,至少在這種情況下是如此。

因此,我剛剛創建了一個包含所有允許的 key/customFilter 過濾器對的聯集。

遊樂場

Values - 建立字典資料結構中所有值的聯集

FilterBuilder:

  1. 對元組 T 中存在的每個元素鍵進行迭代。
  2. 為迭代的鍵創建一個新對象,帶有 key 和 customFilter 屬性。
  3. 其中 customFilter 是一個有兩個參數的函數:item - 被迭代對象的值,applied - 被迭代對象值的陣列

withFilters:是一個柯里化函數。它期望一個我們感興趣的對象元組,並返回一個函數,該函數期望一個 FilterBuilder 結果的陣列。FilterBuilder 只是返回一個所有允許的 key/customFilter 對象狀態的區分聯合。