Я думаю, что следующая версия MergeUnion<T>
может вести себя так, как вы хотите:
type MergeUnion<T> = (
keyof T extends infer K ? [K] extends [keyof T] ? Pick<T, K> & {
[P in Exclude<(T extends any ? keyof T : never), K>]?:
T extends Partial<Record<P, infer V>> ? V : never
} : never : never
) extends infer U ? { [K in keyof U]: U[K] } : never;
type C = MergeUnion<A | B>;
// type C = {
// something: string | string[];
// somethingElse?: number | undefined; }
// }
Это похоже на другой ответ в том, что он находит объединение всех ключей всех составляющих T
(назовите это UnionKeys
, определенное как T extends any ? keyof T : never
) и возвращает сопоставленный тип со всеми ними.Разница в том, что здесь мы также находим пересечение всех ключей всех составляющих T
(назовите его IntersectKeys
, определяемое как просто keyof T
) и разделяем ключи T
на два набора ключей.Один из пересечения присутствует в каждой составляющей, поэтому мы можем просто сделать Pick<T, IntesectKeys>
, чтобы получить общие свойства.Остаток Exclude<UnionKeys, IntersectKeys
> будет необязательным в окончательном типе.
Это довольно уродливо, и я бы убрался, если бы почувствовал себя лучше.Проблема заключается в том, что все еще существует проблема, когда любое из свойств, отображаемых во всех компонентах, является необязательным.Существует ошибка в TypeScript (по состоянию на TS3.5), где в {a?: string} | {a?: number}
свойство a
рассматривается как обязательное свойство, такое как {a: string | number | undefined}
, тогда как оно будетправильнее рассматривать его как необязательный, если какой-либо из компонентов имеет его как необязательный.Эта ошибка доходит до MergeUnion
:
type Oops = MergeUnion<{a?: string} | {a?: number}>
// type Oops = { a: string | number | undefined; }
У меня нет отличного ответа, который не является даже более сложным, поэтому я остановлюсь здесь.Может быть, этого достаточно для ваших нужд.Или, может быть, @ TitianCernicova-Dragomir ответ достаточно для ваших нужд.Надеюсь, что эти ответы помогут вам;удачи!
Ссылка на код