網站搜尋

typescript - 如何合併介面中的所有值


我正在嘗試合併一個介面的所有值,例如:

interface IA {
    a: string;
}
interface IB {
    b: string;
}
interface IC {
    c: string;
}
//...

interface IMap {
    "a": IA;
    "b": IB;
    "c": IC;
    //...
    "z": IZ;
}

有沒有辦法創建一種類型,如:

type IAll = IMap["a"] & IMap["b"] & IMap["c"] & ... & IMap["z"];

我正在嘗試同時使用這個:

type valueMerge<T> = T[keyof T];
type valueMerge2<T extends keyof IMap = keyof IMap> = IMap[T];

// using
let t: valueof<IMap> = null as any;
let t2: valueof2 = null as any;

但對每一個而言,類型是 IA | IB | IC | ... | IZ,而不是 IA & IB & IC & ... & IZ。

答案#1

這類似於關於如何以程式方式將聯集轉換為交集的問題,並且它有類似的解決方案。

通常當你查找一個物件類型的所有屬性時,你會得到所有屬性的聯集,就像你看到的那樣。問題在於 keyof T 是屬性鍵的 聯集,而查找一個鍵的聯集會給你屬性的聯集,因為屬性查找在屬性鍵類型中是 協變 的……但你想要的是在屬性鍵類型中 逆變 的操作。(你可以在這裡閱讀更多關於協變/逆變的內容,但基本上協變意味著聯合集合作為輸入會變成聯合集合作為輸出,而逆變意味著聯集合輸入會變成交集輸出)。

幸運的是,函數參數是逆變的,所以如果我們能將你的介面映射到一個新的介面,其中舊的屬性類型作為函數參數,我們就可以達到一個地方,使這些函數類型的聯集可以導致其參數的交集。而在條件類型中使用 infer 關鍵字的類型推斷可以為我們做到這一點(參見那行說“如果從逆變位置推斷出了任何候選類型,則推斷的類型是這些候選類型的交集”)。

這是代碼:

type IntersectProperties<T> = { [K in keyof T]: (x: T[K]) => void } extends {
  [K in keyof T]: (k: infer I) => void
}
  ? I
  : never;

T 的屬性會對應到函數參數,然後我們會 推斷 一個單一類型 I,對應到 所有的函數參數,這將會是一個交集。

讓我們看看它是否有效:

type IAll = IntersectProperties<IMap>;
// type IAll = IA & IB & IC & ... & IZ

看起來不錯。希望那有幫助;祝你好運!

程式碼連結