Skip to content

Instantly share code, notes, and snippets.

@barchero
Forked from Svehla/deep-merge-typescript.md
Created November 8, 2020 11:19
Show Gist options
  • Select an option

  • Save barchero/d435ca9aeb809d25f5616c83529ffd76 to your computer and use it in GitHub Desktop.

Select an option

Save barchero/d435ca9aeb809d25f5616c83529ffd76 to your computer and use it in GitHub Desktop.
/**
* Take two objects T and U and create the new one with uniq keys for T a U objectI
* helper generic for `DeepMergeTwoTypes`
*/
type GetObjDifferentKeys<T, U> = Omit<T, keyof U> & Omit<U, keyof T>
/**
* Take two objects T and U and create the new one with the same objects keys
* helper generic for `DeepMergeTwoTypes`
*/
type GetObjSameKeys<T, U> = Omit<T | U, keyof GetObjDifferentKeys<T, U>>
type MergeTwoObjects<T, U> =
// non shared keys are optional
Partial<GetObjDifferentKeys<T, U>>
// shared keys are recursively resolved by `DeepMergeTwoTypes<...>`
& {[K in keyof GetObjSameKeys<T, U>]: DeepMergeTwoTypes<T[K], U[K]>}
// it merge 2 static types and try to avoid of unnecessary options (`'`)
export type DeepMergeTwoTypes<T, U> =
// check if generic types are arrays and unwrap it and do the recursion
[T, U] extends [(infer TItem)[], (infer UItem)[]]
? DeepMergeTwoTypes<TItem, UItem>[]
// check if generic types are objects
: [T, U] extends [{ [key: string]: unknown}, { [key: string]: unknown } ]
? MergeTwoObjects<T, U>
: [T, U] extends [{ [key: string]: unknown} | undefined, { [key: string]: unknown } | undefined ]
? MergeTwoObjects<NonNullable<T>, NonNullable<U>> | undefined
: T | U
// -----------------------------------------
// make the generic to apply N objects instead of just 2
type Head<T extends any[]> =
T extends [any, ...any[]]
? T[0]
: never;
type Length<T extends any[]> = T['length'];
type Tail<T extends any[]> =
T extends [any, ...infer TT]
? TT
: [];
type DeepMergeResult<CurrResult, Objects extends any[]> = {
0: CurrResult;
1: DeepMergeResult<DeepMergeTwoTypes<CurrResult, Head<Objects>>, Tail<Objects>>;
} [
Length<Objects> extends 0
? 0
: 1
];
type DeepMergeMany<Args extends any[]> =
Args extends [infer Item1, ... infer Rest]
? DeepMergeResult<Item1, Rest>
: never
// ----------------------------------
const a = {
onlyField: {
a: { b: 'c' }
},
arr: [
[
{ p: 'ahoooj' },
{
next: 'key'
}
]
],
a: {
a: 'a',
b: 'b',
c: {
c: 'c'
},
q: {
a: { b: { c: 'd' } }
}
},
kuba: {
a: {
b: 'b'
}
}
};
const b = {
onlyField: undefined as undefined | null,
arr: [
[
{
cau: 'hi'
}
]
],
a: {
c: {
qqq: { qq: 'qq' },
d: 'd'
},
d: null,
q: { b: { c: 'asdf' } }
},
samo: {
b: {
a: 'a'
}
}
};
const c = {
arr: [
[
{ pozdrav: 'cau' }
]
],
a: {
b: undefined
},
samo: {
a: [
1, undefined, null, 43
]
},
kuba: {
a: {
c: 'c'
}
}
};
const myMerge = <T extends any[]>(...args: T): DeepMergeMany<T> => null as any;
const data = myMerge(
c,
b,
a
).kuba
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment