/** * Merges the two given Records, recursively merging any nested Records with * the second collection overriding the first in case of conflict * * For arrays, maps and sets, a merging strategy can be specified to either * "replace" values, or "merge" them instead. * Use "includeNonEnumerable" option to include non enumerable properties too. * * Example: * * ```ts * import { deepMerge } from "https://deno.land/std@$STD_VERSION/collections/mod.ts"; * import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts"; * * const a = {foo: true} * const b = {foo: {bar: true}} * * assertEquals(deepMerge(a, b), {foo: {bar: true}}); * ``` */ export declare function deepMerge>(record: Partial>, other: Partial>, options?: Readonly): T; export declare function deepMerge, U extends Record, Options extends DeepMergeOptions>(record: Readonly, other: Readonly, options?: Readonly): DeepMerge; /** Merging strategy */ export declare type MergingStrategy = "replace" | "merge"; /** Deep merge options */ export declare type DeepMergeOptions = { /** Merging strategy for arrays */ arrays?: MergingStrategy; /** Merging strategy for Maps */ maps?: MergingStrategy; /** Merging strategy for Sets */ sets?: MergingStrategy; }; /** * How does recursive typing works ? * * Deep merging process is handled through `DeepMerge` type. * If both T and U are Records, we recursively merge them, * else we treat them as primitives. * * Merging process is handled through `Merge` type, in which * we remove all maps, sets, arrays and records so we can handle them * separately depending on merging strategy: * * Merge< * {foo: string}, * {bar: string, baz: Set}, * > // "foo" and "bar" will be handled with `MergeRightOmitComplexes` * // "baz" will be handled with `MergeAll*` type * * `MergeRightOmitComplexes` will do the above: all T's * exclusive keys will be kept, though common ones with U will have their * typing overridden instead: * * MergeRightOmitComplexes< * {foo: string, baz: number}, * {foo: boolean, bar: string} * > // {baz: number, foo: boolean, bar: string} * // "baz" was kept from T * // "foo" was overridden by U's typing * // "bar" was added from U * * For Maps, Arrays, Sets and Records, we use `MergeAll*` utility * types. They will extract relevant data structure from both T and U * (providing that both have same data data structure, except for typing). * * From these, `*ValueType` will extract values (and keys) types to be * able to create a new data structure with an union typing from both * data structure of T and U: * * MergeAllSets< * {foo: Set}, * {foo: Set} * > // `SetValueType` will extract "number" for T * // `SetValueType` will extract "string" for U * // `MergeAllSets` will infer type as Set * // Process is similar for Maps, Arrays, and Sets * * `DeepMerge` is taking a third argument to be handle to * infer final typing depending on merging strategy: * * & (Options extends { sets: "replace" } ? PartialByType> * : MergeAllSets) * * In the above line, if "Options" have its merging strategy for Sets set to * "replace", instead of performing merging of Sets type, it will take the * typing from right operand (U) instead, effectively replacing the typing. * * An additional note, we use `ExpandRecursively` utility type to expand * the resulting typing and hide all the typing logic of deep merging so it is * more user friendly. */ /** Force intellisense to expand the typing to hide merging typings */ declare type ExpandRecursively = T extends Record ? T extends infer O ? { [K in keyof O]: ExpandRecursively; } : never : T; /** Filter of keys matching a given type */ declare type PartialByType = { [K in keyof T as T[K] extends U ? K : never]: T[K]; }; /** Get set values type */ declare type SetValueType = T extends Set ? V : never; /** Merge all sets types definitions from keys present in both objects */ declare type MergeAllSets>, Y = PartialByType>, Z = { [K in keyof X & keyof Y]: Set | SetValueType>; }> = Z; /** Get array values type */ declare type ArrayValueType = T extends Array ? V : never; /** Merge all sets types definitions from keys present in both objects */ declare type MergeAllArrays>, Y = PartialByType>, Z = { [K in keyof X & keyof Y]: Array | ArrayValueType>; }> = Z; /** Get map values types */ declare type MapKeyType = T extends Map ? K : never; /** Get map values types */ declare type MapValueType = T extends Map ? V : never; /** Merge all sets types definitions from keys present in both objects */ declare type MergeAllMaps>, Y = PartialByType>, Z = { [K in keyof X & keyof Y]: Map | MapKeyType, MapValueType | MapValueType>; }> = Z; /** Merge all records types definitions from keys present in both objects */ declare type MergeAllRecords>, Y = PartialByType>, Z = { [K in keyof X & keyof Y]: DeepMerge; }> = Z; /** Exclude map, sets and array from type */ declare type OmitComplexes = Omit | Set | Array | Record>>; /** Object with keys in either T or U but not in both */ declare type ObjectXorKeys & Omit, Y = { [K in keyof X]: X[K]; }> = Y; /** Merge two objects, with left precedence */ declare type MergeRightOmitComplexes & OmitComplexes<{ [K in keyof U]: U[K]; }>> = X; /** Merge two objects */ declare type Merge & MergeAllRecords & (Options extends { sets: "replace"; } ? PartialByType> : MergeAllSets) & (Options extends { arrays: "replace"; } ? PartialByType> : MergeAllArrays) & (Options extends { maps: "replace"; } ? PartialByType> : MergeAllMaps)> = ExpandRecursively; /** Merge deeply two objects */ export declare type DeepMerge> = [ T, U ] extends [Record, Record] ? Merge : // Handle primitives T | U; export {};