網站搜尋

類型 - Typescript:根據枚舉成員的數量獲取具有固定長度的元組


假設我有一個列舉型別

enum Chest {
  ID,
  NAME,
  CAPACITY,
  OPEN_WIN_POINTS,
}

而我想要建立一個元組,其長度與枚舉中的值數量相同。所以對於上述的例子,我想要一個長度為4的字串元組:

type ChestStringTuple = EnumTuple<string, Chest>

const correctTuple: ChestStringTuple = ['foo', 'bar', 'baz', 'goo']
const incorrectTuple1: ChestStringTuple = ['foo', 'bar', 'baz']  // The number of tuple members differs from 4
const incorrectTuple2: ChestStringTuple = ['foo', 'bar', 'baz', false]  // The tuple has non-string members

有沒有辦法取得列舉成員的數量並將其作為元組的長度使用?

答案#1

結合了在 SO 上提出的各種問題的解決方案,我設法創建了一個應該完全符合你需求的解決方法!

首先,讓我們抓取一個可以為我們建立元組的類型吧!

type BuildTuple<Current extends [...T[]], T, Count extends number> =
  Current["length"] extends Count
    ? Current
    : BuildTuple<[T, ...Current], T, Count>

type Tuple<T, Count extends number> = BuildTuple<[], T, Count>

然後我們將跳到這個 SO 問題,它有一些非常有用的聯集輔助函數!

type UnionToIntersection<U> =(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type UnionToOvlds<U> = UnionToIntersection<U extends any ? (f: U) => void : never>;
type PopUnion<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? A : never;

然後我們將會重寫那個 SO 答案中的主要方法,因為我們的目標不是將聯合類型轉換為元組(我不建議這麼做——閱讀這篇文章可以了解原因)。

type UnionCount<U, L extends any[] = []> = {
  0: PopUnion<U> extends infer SELF ? UnionCount<Exclude<U, SELF>, [any, ...L]> : never;
  1: L['length'];
}[[U] extends [never] ? 1 : 0];

從那裡開始,其餘的就相當簡單了。我們會製作幾個別名類型,以便閱讀更容易

type InterfaceLength<T> = UnionCount<keyof T>;
type EnumLength<T> = UnionCount<T>;

如果我們將所有這些結合在一起,我們就可以創建我們的最終枚舉

type InterfaceTuple<T, L> = Tuple<T, InterfaceLength<L>>;
type EnumTuple<T, L> = Tuple<T, EnumLength<L>>;

您的代碼:

enum Chest {
  ID,
  NAME,
  CAPACITY,
  OPEN_WIN_POINTS,
}

type ChestStringTuple = EnumTuple<string, Chest>

const correctTuple: ChestStringTuple = ['foo', 'bar', 'baz', 'goo']    // Valid
const incorrectTuple1: ChestStringTuple = ['foo', 'bar', 'baz']        // Not valid
const incorrectTuple2: ChestStringTuple = ['foo', 'bar', 'baz', false] // Not valid

遊樂場

自行承擔風險使用。