Skip to content

Instantly share code, notes, and snippets.

@acomagu
Last active February 2, 2022 13:47
Show Gist options
  • Save acomagu/b50c4a5a8d9d5956bf9fc77ed1ed25ac to your computer and use it in GitHub Desktop.
Save acomagu/b50c4a5a8d9d5956bf9fc77ed1ed25ac to your computer and use it in GitHub Desktop.
Strongly typed deepmerge implementation.
type IsObject<T> = T extends object ? T extends any[] ? false : true : false;
function isObject<T>(v: T): IsObject<T> {
return (typeof v === 'object' && !Array.isArray(v)) as IsObject<T>;
}
type Merge2<T, U> = IsObject<T> & IsObject<U> extends true ? {
[K in keyof T]: K extends keyof U ? Merge2<T[K], U[K]> : T[K];
} & U : U;
function merge2<T, U>(a: T, b: U): Merge2<T, U> {
return (
isObject(a) && isObject(b)
? Object.assign({}, a, Object.fromEntries(Object.entries(b).map(([k, v]) => [k, merge2((a as any)[k], v)])))
: b
) as Merge2<T, U>;
}
export type Merge<T extends unknown[]> = {
0: T[0],
1: T extends [infer Car, ...infer Cdr] ? Merge2<Car, Merge<Cdr>> : T,
}[T extends [unknown, unknown, ...unknown[]] ? 1 : 0];
export function merge<T extends unknown[]>(...objs: T): Merge<T> {
if (objs.length < 2) return objs[0];
return merge2(objs[0], merge(...objs.slice(1)));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment