網站搜尋

typescript - 如何實現一個工具型別來把多個介面使用 Object.assign 邏輯合併成一個?


我需要一個工具類型,能夠將傳入聯合中的兩個或更多接口合併成一個,不將重複的屬性解析為 never,而是進行覆蓋。它應該像 Object.assign 一樣工作,但作用於類型/接口。假設我們有三個類型:

    type A = {a: number};
    type B = {a: boolean, b: boolean};
    type C = {c: boolean};

我想要一些可以將它們合併成一個的工具:

type MergedType = MergeTypes<A|B|C>;

在結果中,我想要得到以下介面:

type MergedType = {
  a: boolean, // overwriting, not `never` in case of using intersection (A & B & C)
  b: boolean,
  c: boolean
}

有沒有辦法達到這種行為?我只能為兩個帶有不同泛型參數的介面寫一個工具函數

  type MergeTypes<S, T> = Omit<S, keyof T> & T;

  type MergedType = MergeTypes<A, B>;

但我完全不知道如何在聯合上迭代,並參考前一個參數來達到陣列 reduce 的邏輯。提前感謝你!

答案#1

type Pure<T> = { [K in keyof T]: T[K] } & unknown
type Check<T, Eq extends ([T, V] extends [V, T] ? '=' : '!='), V> = T

type Assign<T, U> = Pure<Omit<T, keyof U> & U>
type AssignList<A extends any[]> =
  | A extends [infer F] ? F
  : A extends [...infer F, infer L] ? Assign<AssignList<F>, L>
  : never

type checkAssignList = {
  case1: Check<AssignList<[{ a: 1 }]>, '=', { a: 1 }>,
  // ^?
  case2: Check<AssignList<[{ a: 1 }, { a: 2 }]>, '=', { a: 2 }>,
  // ^?
  case3: Check<AssignList<[{ a: 1 }, { a: 2 }, { a: 3 }]>, '=', { a: 3 }>,
  // ^?
  case4: Check<AssignList<[{ a: 1 }, { b: 2 }, { a: 3 }]>, '=', { a: 3, b: 2 }>,
  // ^?
}

const assign = Object.assign as <T extends {}, A extends readonly any[]>(target: T, ...sources: A) => AssignList<[T, ...A]>

const checkAssignList = {
  case1: assign({ a: 1 }),
  // ^?
  case2: assign({ a: 1 }, { a: 2 }),
  // ^?
  case3: assign({ a: 1 }, { a: 2 }, { a: 3 }),
  // ^?
  case4: assign({ a: 1 }, { b: 2 }, { a: 3 }),
  // ^?
}
console.log(checkAssignList)