Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: provide customizable meta data to custom merge functions
Browse files Browse the repository at this point in the history
fix #33
RebeccaStevens committed Feb 17, 2022

Verified

This commit was signed with the committer’s verified signature.
bukka Jakub Zelenka
1 parent aef30eb commit 9831269
Showing 11 changed files with 682 additions and 135 deletions.
18 changes: 12 additions & 6 deletions docs/API.md
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ Merges the array of inputs together using the default configuration.

Note: If `inputs` isn't typed as a tuple then we cannot determine the output type. The output type will simply be `unknown`.

## deepmergeCustom(options)
## deepmergeCustom(options[, rootMetaData])

Generate a customized deepmerge function using the given options. The returned function works just like `deepmerge` except it uses the customized configuration.

@@ -21,38 +21,44 @@ All these options are optional.

#### `mergeRecords`

Type: `false | (values: Record<any, unknown>[], utils: DeepMergeMergeFunctionUtils) => unknown`
Type: `false | (values: Record<any, unknown>[], utils: DeepMergeMergeFunctionUtils, meta: MetaData) => unknown`

If false, records won't be merged. If set to a function, that function will be used to merge records.

Note: Records are "vanilla" objects (e.g. `{ foo: "hello", bar: "world" }`).

#### `mergeArrays`

Type: `false | (values: unknown[][], utils: DeepMergeMergeFunctionUtils) => unknown`
Type: `false | (values: unknown[][], utils: DeepMergeMergeFunctionUtils, meta: MetaData) => unknown`

If false, arrays won't be merged. If set to a function, that function will be used to merge arrays.

#### `mergeMaps`

Type: `false | (values: Map<unknown, unknown>[], utils: DeepMergeMergeFunctionUtils) => unknown`
Type: `false | (values: Map<unknown, unknown>[], utils: DeepMergeMergeFunctionUtils, meta: MetaData) => unknown`

If false, maps won't be merged. If set to a function, that function will be used to merge maps.

#### `mergeSets`

Type: `false | (values: Set<unknown>[], utils: DeepMergeMergeFunctionUtils) => unknown`
Type: `false | (values: Set<unknown>[], utils: DeepMergeMergeFunctionUtils, meta: MetaData) => unknown`

If false, sets won't be merged. If set to a function, that function will be used to merge sets.

#### `mergeOthers`

Type: `(values: Set<unknown>[], utils: DeepMergeMergeFunctionUtils) => unknown`
Type: `(values: Set<unknown>[], utils: DeepMergeMergeFunctionUtils, meta: MetaData) => unknown`

If set to a function, that function will be used to merge everything else.

Note: This includes merging mixed types, such as merging a map with an array.

### `rootMetaData`

Type: `MetaData`

The given meta data value will be passed to root level merges.

### DeepMergeMergeFunctionUtils

This is a set of utility functions that are made available to your custom merge functions.
121 changes: 117 additions & 4 deletions docs/deepmergeCustom.md
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ This can be done using [Declaration Merging](https://www.typescriptlang.org/docs

```ts
declare module "deepmerge-ts" {
interface DeepMergeMergeFunctionURItoKind<Ts extends ReadonlyArray<unknown>, MF extends DeepMergeMergeFunctionsURIs> {
interface DeepMergeMergeFunctionURItoKind<Ts extends ReadonlyArray<unknown>, MF extends DeepMergeMergeFunctionsURIs, M> {
readonly MyCustomMergeURI: MyValue;
}
}
@@ -52,13 +52,13 @@ import { deepmergeCustom } from "deepmerge-ts";
const customizedDeepmerge = deepmergeCustom<{
DeepMergeOthersURI: "MyDeepMergeDatesURI"; // <-- Needed for correct output type.
}>({
mergeOthers: (values, utils) => {
mergeOthers: (values, utils, meta) => {
// If every value is a date, the return the amalgamated array.
if (values.every((value) => value instanceof Date)) {
return values;
}
// Otherwise, use the default merging strategy.
return utils.defaultMergeFunctions.mergeOthers(values, utils);
return utils.defaultMergeFunctions.mergeOthers(values);
},
});

@@ -71,7 +71,8 @@ customDeepmerge(x, y, z); // => { foo: [Date, Date, Date] }
declare module "deepmerge-ts" {
interface DeepMergeMergeFunctionURItoKind<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
MF extends DeepMergeMergeFunctionsURIs,
M
> {
readonly MyDeepMergeDatesURI: EveryIsDate<Ts> extends true ? Ts : DeepMergeLeaf<Ts>;
}
@@ -86,6 +87,118 @@ type EveryIsDate<Ts extends ReadonlyArray<unknown>> = Ts extends readonly [infer

Note: If you want to use HKTs in your own project, not related to deepmerge-ts, we recommend checking out [fp-ts](https://gcanti.github.io/fp-ts/modules/HKT.ts.html).

## Meta Data

We provide a simple object of meta data that states the key that the values being merged were under.

Here's an example that creates a custom deepmerge function that merges numbers differently based on the key they were under.

```ts
import type { DeepMergeLeaf, DeepMergeMergeFunctionURItoKind, DeepMergeMergeFunctionsURIs } from "deepmerge-ts";
import { deepmergeCustom } from "deepmerge-ts";

const customizedDeepmerge = deepmergeCustom({
mergeOthers: (values, utils, meta) => {
if (meta !== undefined && areAllNumbers(values)) {
const { key } = meta;
const numbers: ReadonlyArray<number> = values;

if (key === "sum") {
return numbers.reduce((sum, value) => sum + value);
}
if (key === "product") {
return numbers.reduce((prod, value) => prod * value);
}
if (key === "mean") {
return numbers.reduce((sum, value) => sum + value) / numbers.length;
}
}

return utils.defaultMergeFunctions.mergeOthers(values);
},
});

function areAllNumbers(values: ReadonlyArray<unknown>): values is ReadonlyArray<number> {
return values.every((value) => typeof value === "number");
}

const v = { sum: 1, product: 2, mean: 3 };
const x = { sum: 4, product: 5, mean: 6 };
const y = { sum: 7, product: 8, mean: 9 };
const z = { sum: 10, product: 11, mean: 12 };

customizedDeepmerge(v, x, y, z); // => { sum: 22, product: 880, mean: 7.5 }
```

### Customizing the Meta Data

You can customize the meta data that is passed to the merge functions by providing a `metaDataUpdater` function.

Here's an example that uses custom metadata that accumulates the full key path.

```ts
import type { DeepMergeLeaf, DeepMergeMergeFunctionURItoKind, DeepMergeMergeFunctionsURIs } from "deepmerge-ts";
import { deepmergeCustom } from "deepmerge-ts";

const customizedDeepmerge = deepmergeCustom<
// Change the return type of `mergeOthers`.
{
DeepMergeOthersURI: "KeyPathBasedMerge";
},
// Change the meta data type.
{
keyPath: ReadonlyArray<PropertyKey>;
}
>({
// Customize what the actual meta data.
metaDataUpdater: (previousMeta, metaMeta) => {
if (previousMeta === undefined) {
return { keyPath: [] };
}
return {
...metaMeta,
keyPath: [...previousMeta.keyPath, metaMeta.key],
};
},
// Use the meta data when merging others.
mergeOthers: (values, utils, meta) => {
if (
meta !== undefined &&
meta.keyPath.length >= 2 &&
meta.keyPath[meta.keyPath.length - 2] === "bar" &&
meta.keyPath[meta.keyPath.length - 1] === "baz"
) {
return "special merge";
}

return utils.defaultMergeFunctions.mergeOthers(values);
},
});

const x = {
foo: { bar: { baz: 1, qux: 2 } },
bar: { baz: 3, qux: 4 },
};
const y = {
foo: { bar: { baz: 5, bar: { baz: 6, qux: 7 } } },
bar: { baz: 8, qux: 9 },
};

customizedDeepmerge(x, y); // => { foo: { bar: { baz: "special merge", bar: { baz: 6, qux: 7 }, qux: 2 } }, bar: { baz: "special merge", qux: 9 }, }

declare module "../src/types" {
interface DeepMergeMergeFunctionURItoKind<
Ts extends Readonly<ReadonlyArray<unknown>>,
MF extends DeepMergeMergeFunctionsURIs,
M // This is the meta data type
> {
readonly KeyPathBasedMerge: Ts[number] extends number
? Ts[number] | string
: DeepMergeLeaf<Ts>;
}
}
```

## API

[See deepmerge custom API](./API.md#deepmergecustomoptions).
174 changes: 131 additions & 43 deletions src/deepmerge.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
DeepMergeBuiltInMetaData,
DeepMergeHKT,
DeepMergeArraysDefaultHKT,
DeepMergeMergeFunctionsDefaultURIs,
@@ -26,6 +27,16 @@ const defaultMergeFunctions = {
mergeOthers: leaf,
} as const;

/**
* The default function to update meta data.
*/
function defaultMetaDataUpdater<M>(
previousMeta: M,
metaMeta: DeepMergeBuiltInMetaData
): DeepMergeBuiltInMetaData {
return metaMeta;
}

/**
* The default merge functions.
*/
@@ -36,12 +47,17 @@ export type DeepMergeMergeFunctionsDefaults = typeof defaultMergeFunctions;
*
* @param objects - The objects to merge.
*/
export function deepmerge<Ts extends ReadonlyArray<unknown>>(
export function deepmerge<Ts extends Readonly<ReadonlyArray<unknown>>>(
...objects: readonly [...Ts]
): DeepMergeHKT<Ts, DeepMergeMergeFunctionsDefaultURIs> {
): DeepMergeHKT<
Ts,
DeepMergeMergeFunctionsDefaultURIs,
DeepMergeBuiltInMetaData
> {
return deepmergeCustom({})(...objects) as DeepMergeHKT<
Ts,
DeepMergeMergeFunctionsDefaultURIs
DeepMergeMergeFunctionsDefaultURIs,
DeepMergeBuiltInMetaData
>;
}

@@ -53,18 +69,55 @@ export function deepmerge<Ts extends ReadonlyArray<unknown>>(
export function deepmergeCustom<
PMF extends Partial<DeepMergeMergeFunctionsURIs>
>(
options: DeepMergeOptions
options: DeepMergeOptions<DeepMergeBuiltInMetaData, DeepMergeBuiltInMetaData>
): <Ts extends ReadonlyArray<unknown>>(
...objects: Ts
) => DeepMergeHKT<
Ts,
GetDeepMergeMergeFunctionsURIs<PMF>,
DeepMergeBuiltInMetaData
>;

/**
* Deeply merge two or more objects using the given options and meta data.
*
* @param options - The options on how to customize the merge function.
* @param rootMetaData - The meta data passed to the root items' being merged.
*/
export function deepmergeCustom<
PMF extends Partial<DeepMergeMergeFunctionsURIs>,
MetaData,
MetaMetaData extends Readonly<
Record<PropertyKey, unknown>
> = DeepMergeBuiltInMetaData
>(
options: DeepMergeOptions<MetaData, MetaMetaData>,
rootMetaData?: MetaData
): <Ts extends ReadonlyArray<unknown>>(
...objects: Ts
) => DeepMergeHKT<Ts, GetDeepMergeMergeFunctionsURIs<PMF>, MetaData>;

export function deepmergeCustom<
PMF extends Partial<DeepMergeMergeFunctionsURIs>,
MetaData,
MetaMetaData extends Readonly<Record<PropertyKey, unknown>>
>(
options: DeepMergeOptions<MetaData>,
rootMetaData?: MetaData
): <Ts extends ReadonlyArray<unknown>>(
...objects: Ts
) => DeepMergeHKT<Ts, GetDeepMergeMergeFunctionsURIs<PMF>> {
) => DeepMergeHKT<Ts, GetDeepMergeMergeFunctionsURIs<PMF>, MetaData> {
/**
* The type of the customized deepmerge function.
*/
type CustomizedDeepmerge = <Ts extends ReadonlyArray<unknown>>(
...objects: Ts
) => DeepMergeHKT<Ts, GetDeepMergeMergeFunctionsURIs<PMF>>;
) => DeepMergeHKT<Ts, GetDeepMergeMergeFunctionsURIs<PMF>, MetaData>;

const utils = getUtils(options, customizedDeepmerge as CustomizedDeepmerge);
const utils: DeepMergeMergeFunctionUtils<MetaData, MetaMetaData> = getUtils(
options,
customizedDeepmerge as CustomizedDeepmerge
);

/**
* The customized deepmerge function.
@@ -77,7 +130,13 @@ export function deepmergeCustom<
return objects[0];
}

return mergeUnknowns(objects, utils);
return mergeUnknowns<
ReadonlyArray<unknown>,
typeof utils,
GetDeepMergeMergeFunctionsURIs<PMF>,
MetaData,
MetaMetaData
>(objects, utils, rootMetaData);
}

return customizedDeepmerge as CustomizedDeepmerge;
@@ -88,20 +147,29 @@ export function deepmergeCustom<
*
* @param options - The options the user specified
*/
function getUtils(
options: DeepMergeOptions,
customizedDeepmerge: DeepMergeMergeFunctionUtils["deepmerge"]
): DeepMergeMergeFunctionUtils {
function getUtils<M, MM extends Readonly<Record<PropertyKey, unknown>>>(
options: DeepMergeOptions<M>,
customizedDeepmerge: DeepMergeMergeFunctionUtils<M, MM>["deepmerge"]
): DeepMergeMergeFunctionUtils<M, MM> {
return {
defaultMergeFunctions,
mergeFunctions: {
...defaultMergeFunctions,
...Object.fromEntries(
Object.entries(options).map(([key, option]) =>
option === false ? [key, leaf] : [key, option]
)
Object.entries(options)
.filter(([key, option]) =>
Object.prototype.hasOwnProperty.call(defaultMergeFunctions, key)
)
.map(([key, option]) =>
option === false ? [key, leaf] : [key, option]
)
),
} as DeepMergeMergeFunctionUtils["mergeFunctions"],
} as DeepMergeMergeFunctionUtils<M, MM>["mergeFunctions"],
metaDataUpdater: (options.metaDataUpdater ??
defaultMetaDataUpdater) as unknown as DeepMergeMergeFunctionUtils<
M,
MM
>["metaDataUpdater"],
deepmerge: customizedDeepmerge,
};
}
@@ -113,9 +181,11 @@ function getUtils(
*/
function mergeUnknowns<
Ts extends ReadonlyArray<unknown>,
U extends DeepMergeMergeFunctionUtils,
MF extends DeepMergeMergeFunctionsURIs
>(values: Ts, utils: U): DeepMergeHKT<Ts, MF> {
U extends DeepMergeMergeFunctionUtils<M, MM>,
MF extends DeepMergeMergeFunctionsURIs,
M,
MM extends Readonly<Record<PropertyKey, unknown>>
>(values: Ts, utils: U, meta: M | undefined): DeepMergeHKT<Ts, MF, M> {
const type = getObjectType(values[0]);

// eslint-disable-next-line functional/no-conditional-statement -- add an early escape for better performance.
@@ -126,43 +196,49 @@ function mergeUnknowns<
continue;
}

return utils.mergeFunctions.mergeOthers(values, utils) as DeepMergeHKT<
Ts,
MF
>;
return utils.mergeFunctions.mergeOthers(
values,
utils,
meta
) as DeepMergeHKT<Ts, MF, M>;
}
}

switch (type) {
case ObjectType.RECORD:
return utils.mergeFunctions.mergeRecords(
values as ReadonlyArray<Readonly<Record<PropertyKey, unknown>>>,
utils
) as DeepMergeHKT<Ts, MF>;
utils,
meta
) as DeepMergeHKT<Ts, MF, M>;

case ObjectType.ARRAY:
return utils.mergeFunctions.mergeArrays(
values as ReadonlyArray<ReadonlyArray<unknown>>,
utils
) as DeepMergeHKT<Ts, MF>;
values as ReadonlyArray<Readonly<ReadonlyArray<unknown>>>,
utils,
meta
) as DeepMergeHKT<Ts, MF, M>;

case ObjectType.SET:
return utils.mergeFunctions.mergeSets(
values as ReadonlyArray<Readonly<ReadonlySet<unknown>>>,
utils
) as DeepMergeHKT<Ts, MF>;
utils,
meta
) as DeepMergeHKT<Ts, MF, M>;

case ObjectType.MAP:
return utils.mergeFunctions.mergeMaps(
values as ReadonlyArray<Readonly<ReadonlyMap<unknown, unknown>>>,
utils
) as DeepMergeHKT<Ts, MF>;
utils,
meta
) as DeepMergeHKT<Ts, MF, M>;

default:
return utils.mergeFunctions.mergeOthers(values, utils) as DeepMergeHKT<
Ts,
MF
>;
return utils.mergeFunctions.mergeOthers(
values,
utils,
meta
) as DeepMergeHKT<Ts, MF, M>;
}
}

@@ -173,9 +249,11 @@ function mergeUnknowns<
*/
function mergeRecords<
Ts extends ReadonlyArray<Record<PropertyKey, unknown>>,
U extends DeepMergeMergeFunctionUtils,
MF extends DeepMergeMergeFunctionsURIs
>(values: Ts, utils: U) {
U extends DeepMergeMergeFunctionUtils<M, MM>,
MF extends DeepMergeMergeFunctionsURIs,
M,
MM extends DeepMergeBuiltInMetaData
>(values: Ts, utils: U, meta: M | undefined) {
const result: Record<PropertyKey, unknown> = {};

/* eslint-disable functional/no-loop-statement, functional/no-conditional-statement -- using a loop here is more performant. */
@@ -191,15 +269,24 @@ function mergeRecords<

// assert(propValues.length > 0);

const updatedMeta = utils.metaDataUpdater(meta, {
key,
parents: values,
} as unknown as MM);

result[key] =
propValues.length === 1
? propValues[0]
: mergeUnknowns(propValues, utils);
: mergeUnknowns<ReadonlyArray<unknown>, U, MF, M, MM>(
propValues,
utils,
updatedMeta
);
}

/* eslint-enable functional/no-loop-statement, functional/no-conditional-statement */

return result as DeepMergeRecordsDefaultHKT<Ts, MF>;
return result as DeepMergeRecordsDefaultHKT<Ts, MF, M>;
}

/**
@@ -209,9 +296,10 @@ function mergeRecords<
*/
function mergeArrays<
Ts extends ReadonlyArray<ReadonlyArray<unknown>>,
MF extends DeepMergeMergeFunctionsURIs
MF extends DeepMergeMergeFunctionsURIs,
M
>(values: Ts) {
return values.flat() as DeepMergeArraysDefaultHKT<Ts, MF>;
return values.flat() as DeepMergeArraysDefaultHKT<Ts, MF, M>;
}

/**
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ export { deepmerge, deepmergeCustom } from "./deepmerge";
export type { DeepMergeMergeFunctionsDefaults } from "./deepmerge";
export type {
DeepMergeArraysDefaultHKT,
DeepMergeBuiltInMetaData,
DeepMergeHKT,
DeepMergeLeaf,
DeepMergeLeafHKT,
50 changes: 32 additions & 18 deletions src/types/defaults.ts
Original file line number Diff line number Diff line change
@@ -57,11 +57,12 @@ type BlacklistedRecordProps = "__proto__";
*/
export type DeepMergeRecordsDefaultHKT<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
> = Ts extends readonly [unknown, ...ReadonlyArray<unknown>]
MF extends DeepMergeMergeFunctionsURIs,
M
> = Ts extends Readonly<readonly [unknown, ...Readonly<ReadonlyArray<unknown>>]>
? FlatternAlias<
Omit<
DeepMergeRecordsDefaultHKTInternalProps<Ts, MF>,
DeepMergeRecordsDefaultHKTInternalProps<Ts, MF, M>,
BlacklistedRecordProps
>
>
@@ -72,16 +73,19 @@ export type DeepMergeRecordsDefaultHKT<
*/
type DeepMergeRecordsDefaultHKTInternalProps<
Ts extends readonly [unknown, ...ReadonlyArray<unknown>],
MF extends DeepMergeMergeFunctionsURIs
MF extends DeepMergeMergeFunctionsURIs,
M
> = {
[K in OptionalKeysOf<Ts>]?: DeepMergeHKT<
DeepMergeRecordsDefaultHKTInternalPropValue<Ts, K>,
MF
DeepMergeRecordsDefaultHKTInternalPropValue<Ts, K, M>,
MF,
M
>;
} & {
[K in RequiredKeysOf<Ts>]: DeepMergeHKT<
DeepMergeRecordsDefaultHKTInternalPropValue<Ts, K>,
MF
DeepMergeRecordsDefaultHKTInternalPropValue<Ts, K, M>,
MF,
M
>;
};

@@ -90,9 +94,15 @@ type DeepMergeRecordsDefaultHKTInternalProps<
*/
type DeepMergeRecordsDefaultHKTInternalPropValue<
Ts extends readonly [unknown, ...ReadonlyArray<unknown>],
K extends PropertyKey
K extends PropertyKey,
M
> = FilterOutNever<
DeepMergeRecordsDefaultHKTInternalPropValueHelper<Ts, K, readonly []>
DeepMergeRecordsDefaultHKTInternalPropValueHelper<
Ts,
K,
M,
Readonly<readonly []>
>
>;

/**
@@ -101,13 +111,15 @@ type DeepMergeRecordsDefaultHKTInternalPropValue<
type DeepMergeRecordsDefaultHKTInternalPropValueHelper<
Ts extends readonly [unknown, ...ReadonlyArray<unknown>],
K extends PropertyKey,
M,
Acc extends ReadonlyArray<unknown>
> = Ts extends readonly [infer Head, ...infer Rest]
? Head extends Record<PropertyKey, unknown>
? Rest extends readonly [unknown, ...ReadonlyArray<unknown>]
? DeepMergeRecordsDefaultHKTInternalPropValueHelper<
Rest,
K,
M,
[...Acc, ValueOfKey<Head, K>]
>
: [...Acc, ValueOfKey<Head, K>]
@@ -119,23 +131,25 @@ type DeepMergeRecordsDefaultHKTInternalPropValueHelper<
*/
export type DeepMergeArraysDefaultHKT<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
> = DeepMergeArraysDefaultHKTHelper<Ts, MF, []>;
MF extends DeepMergeMergeFunctionsURIs,
M
> = DeepMergeArraysDefaultHKTHelper<Ts, MF, M, []>;

/**
* Tail-recursive helper type for DeepMergeArraysDefaultHKT.
*/
type DeepMergeArraysDefaultHKTHelper<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs,
M,
Acc extends ReadonlyArray<unknown>
> = Ts extends readonly [infer Head, ...infer Rest]
? Head extends ReadonlyArray<unknown>
? Rest extends readonly [
ReadonlyArray<unknown>,
...ReadonlyArray<ReadonlyArray<unknown>>
]
? DeepMergeArraysDefaultHKTHelper<Rest, MF, [...Acc, ...Head]>
? DeepMergeArraysDefaultHKTHelper<Rest, MF, M, [...Acc, ...Head]>
: [...Acc, ...Head]
: never
: never;
@@ -164,35 +178,35 @@ export type GetDeepMergeMergeFunctionsURIs<
// prettier-ignore
DeepMergeRecordsURI:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
PMF["DeepMergeRecordsURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any>
PMF["DeepMergeRecordsURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any, any>
? PMF["DeepMergeRecordsURI"]
: DeepMergeRecordsDefaultURI;

// prettier-ignore
DeepMergeArraysURI:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
PMF["DeepMergeArraysURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any>
PMF["DeepMergeArraysURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any, any>
? PMF["DeepMergeArraysURI"]
: DeepMergeArraysDefaultURI;

// prettier-ignore
DeepMergeSetsURI:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
PMF["DeepMergeSetsURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any>
PMF["DeepMergeSetsURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any, any>
? PMF["DeepMergeSetsURI"]
: DeepMergeSetsDefaultURI;

// prettier-ignore
DeepMergeMapsURI:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
PMF["DeepMergeMapsURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any>
PMF["DeepMergeMapsURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any, any>
? PMF["DeepMergeMapsURI"]
: DeepMergeMapsDefaultURI;

// prettier-ignore
DeepMergeOthersURI:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
PMF["DeepMergeOthersURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any>
PMF["DeepMergeOthersURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any, any>
? PMF["DeepMergeOthersURI"]
: DeepMergeLeafURI;
}>;
61 changes: 39 additions & 22 deletions src/types/merging.ts
Original file line number Diff line number Diff line change
@@ -19,11 +19,12 @@ import type {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export interface DeepMergeMergeFunctionURItoKind<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
MF extends DeepMergeMergeFunctionsURIs,
M
> {
readonly DeepMergeLeafURI: DeepMergeLeaf<Ts>;
readonly DeepMergeRecordsDefaultURI: DeepMergeRecordsDefaultHKT<Ts, MF>;
readonly DeepMergeArraysDefaultURI: DeepMergeArraysDefaultHKT<Ts, MF>;
readonly DeepMergeRecordsDefaultURI: DeepMergeRecordsDefaultHKT<Ts, MF, M>;
readonly DeepMergeArraysDefaultURI: DeepMergeArraysDefaultHKT<Ts, MF, M>;
readonly DeepMergeSetsDefaultURI: DeepMergeSetsDefaultHKT<Ts>;
readonly DeepMergeMapsDefaultURI: DeepMergeMapsDefaultHKT<Ts>;
}
@@ -34,15 +35,17 @@ export interface DeepMergeMergeFunctionURItoKind<
type DeepMergeMergeFunctionKind<
URI extends DeepMergeMergeFunctionURIs,
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
> = DeepMergeMergeFunctionURItoKind<Ts, MF>[URI];
MF extends DeepMergeMergeFunctionsURIs,
M
> = DeepMergeMergeFunctionURItoKind<Ts, MF, M>[URI];

/**
* A union of all valid merge function URIs.
*/
type DeepMergeMergeFunctionURIs = keyof DeepMergeMergeFunctionURItoKind<
ReadonlyArray<unknown>,
DeepMergeMergeFunctionsURIs
DeepMergeMergeFunctionsURIs,
unknown
>;

/**
@@ -80,62 +83,68 @@ export type DeepMergeMergeFunctionsURIs = Readonly<{
*/
export type DeepMergeHKT<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
MF extends DeepMergeMergeFunctionsURIs,
M
> = IsTuple<Ts> extends true
? Ts extends readonly []
? undefined
: Ts extends readonly [infer T1]
? T1
: EveryIsArray<Ts> extends true
? DeepMergeArraysHKT<Ts, MF>
? DeepMergeArraysHKT<Ts, MF, M>
: EveryIsMap<Ts> extends true
? DeepMergeMapsHKT<Ts, MF>
? DeepMergeMapsHKT<Ts, MF, M>
: EveryIsSet<Ts> extends true
? DeepMergeSetsHKT<Ts, MF>
? DeepMergeSetsHKT<Ts, MF, M>
: EveryIsRecord<Ts> extends true
? DeepMergeRecordsHKT<Ts, MF>
: DeepMergeOthersHKT<Ts, MF>
? DeepMergeRecordsHKT<Ts, MF, M>
: DeepMergeOthersHKT<Ts, MF, M>
: unknown;

/**
* Deep merge records.
*/
type DeepMergeRecordsHKT<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
> = DeepMergeMergeFunctionKind<MF["DeepMergeRecordsURI"], Ts, MF>;
MF extends DeepMergeMergeFunctionsURIs,
M
> = DeepMergeMergeFunctionKind<MF["DeepMergeRecordsURI"], Ts, MF, M>;

/**
* Deep merge arrays.
*/
type DeepMergeArraysHKT<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
> = DeepMergeMergeFunctionKind<MF["DeepMergeArraysURI"], Ts, MF>;
MF extends DeepMergeMergeFunctionsURIs,
M
> = DeepMergeMergeFunctionKind<MF["DeepMergeArraysURI"], Ts, MF, M>;

/**
* Deep merge sets.
*/
type DeepMergeSetsHKT<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
> = DeepMergeMergeFunctionKind<MF["DeepMergeSetsURI"], Ts, MF>;
MF extends DeepMergeMergeFunctionsURIs,
M
> = DeepMergeMergeFunctionKind<MF["DeepMergeSetsURI"], Ts, MF, M>;

/**
* Deep merge maps.
*/
type DeepMergeMapsHKT<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
> = DeepMergeMergeFunctionKind<MF["DeepMergeMapsURI"], Ts, MF>;
MF extends DeepMergeMergeFunctionsURIs,
M
> = DeepMergeMergeFunctionKind<MF["DeepMergeMapsURI"], Ts, MF, M>;

/**
* Deep merge other things.
*/
type DeepMergeOthersHKT<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
> = DeepMergeMergeFunctionKind<MF["DeepMergeOthersURI"], Ts, MF>;
MF extends DeepMergeMergeFunctionsURIs,
M
> = DeepMergeMergeFunctionKind<MF["DeepMergeOthersURI"], Ts, MF, M>;

/**
* The merge function that returns a leaf.
@@ -165,3 +174,11 @@ export type DeepMergeLeaf<Ts extends ReadonlyArray<unknown>> =
: never
: Tail
: never;

/**
* The meta data deepmerge is able to provide.
*/
export type DeepMergeBuiltInMetaData = Readonly<{
key: PropertyKey;
parents: ReadonlyArray<Readonly<Record<PropertyKey, unknown>>>;
}>;
66 changes: 46 additions & 20 deletions src/types/options.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,97 @@
import type { DeepMergeMergeFunctionsDefaults } from "@/deepmerge";

import type { DeepMergeBuiltInMetaData } from "./merging";

/**
* The options the user can pass to customize deepmerge.
*/
export type DeepMergeOptions = Partial<DeepMergeOptionsFull>;
export type DeepMergeOptions<
M,
MM extends Record<PropertyKey, unknown> = DeepMergeBuiltInMetaData
> = Partial<DeepMergeOptionsFull<M, MM & Partial<DeepMergeBuiltInMetaData>>>;

type MetaDataUpdater<M, MM extends Record<PropertyKey, unknown>> = (
previousMeta: M | undefined,
metaMeta: MM
) => M;

/**
* All the options the user can pass to customize deepmerge.
*/
type DeepMergeOptionsFull = Readonly<{
mergeRecords: DeepMergeMergeFunctions["mergeRecords"] | false;
mergeArrays: DeepMergeMergeFunctions["mergeArrays"] | false;
mergeMaps: DeepMergeMergeFunctions["mergeMaps"] | false;
mergeSets: DeepMergeMergeFunctions["mergeSets"] | false;
mergeOthers: DeepMergeMergeFunctions["mergeOthers"];
type DeepMergeOptionsFull<
M,
MM extends Record<PropertyKey, unknown>
> = Readonly<{
mergeRecords: DeepMergeMergeFunctions<M, MM>["mergeRecords"] | false;
mergeArrays: DeepMergeMergeFunctions<M, MM>["mergeArrays"] | false;
mergeMaps: DeepMergeMergeFunctions<M, MM>["mergeMaps"] | false;
mergeSets: DeepMergeMergeFunctions<M, MM>["mergeSets"] | false;
mergeOthers: DeepMergeMergeFunctions<M, MM>["mergeOthers"];
metaDataUpdater: MetaDataUpdater<M, MM>;
}>;

/**
* All the merge functions that deepmerge uses.
*/
type DeepMergeMergeFunctions = Readonly<{
type DeepMergeMergeFunctions<
M,
MM extends Record<PropertyKey, unknown>
> = Readonly<{
mergeRecords: <
Ts extends ReadonlyArray<Readonly<Record<PropertyKey, unknown>>>,
U extends DeepMergeMergeFunctionUtils
U extends DeepMergeMergeFunctionUtils<M, MM>
>(
records: Ts,
utils: U
utils: U,
meta: M | undefined
) => unknown;

mergeArrays: <
Ts extends ReadonlyArray<ReadonlyArray<unknown>>,
U extends DeepMergeMergeFunctionUtils
U extends DeepMergeMergeFunctionUtils<M, MM>
>(
records: Ts,
utils: U
utils: U,
meta: M | undefined
) => unknown;

mergeMaps: <
Ts extends ReadonlyArray<Readonly<ReadonlyMap<unknown, unknown>>>,
U extends DeepMergeMergeFunctionUtils
U extends DeepMergeMergeFunctionUtils<M, MM>
>(
records: Ts,
utils: U
utils: U,
meta: M | undefined
) => unknown;

mergeSets: <
Ts extends ReadonlyArray<Readonly<ReadonlySet<unknown>>>,
U extends DeepMergeMergeFunctionUtils
U extends DeepMergeMergeFunctionUtils<M, MM>
>(
records: Ts,
utils: U
utils: U,
meta: M | undefined
) => unknown;

mergeOthers: <
Ts extends ReadonlyArray<unknown>,
U extends DeepMergeMergeFunctionUtils
U extends DeepMergeMergeFunctionUtils<M, MM>
>(
records: Ts,
utils: U
utils: U,
meta: M | undefined
) => unknown;
}>;

/**
* The utils provided to the merge functions.
*/
export type DeepMergeMergeFunctionUtils = Readonly<{
mergeFunctions: DeepMergeMergeFunctions;
export type DeepMergeMergeFunctionUtils<
M,
MM extends Record<PropertyKey, unknown>
> = Readonly<{
mergeFunctions: DeepMergeMergeFunctions<M, MM>;
defaultMergeFunctions: DeepMergeMergeFunctionsDefaults;
metaDataUpdater: MetaDataUpdater<M, MM>;
deepmerge: <Ts extends ReadonlyArray<unknown>>(...values: Ts) => unknown;
}>;
4 changes: 3 additions & 1 deletion tests/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -24,6 +24,8 @@
"functional/prefer-readonly-type": "off",
"import/no-relative-parent-imports": "off",
"jsdoc/require-jsdoc": "off",
"sonarjs/no-duplicate-string": "off"
"sonarjs/no-duplicate-string": "off",
"sonarjs/no-identical-functions": "off",
"unicorn/consistent-function-scoping": "off"
}
}
279 changes: 271 additions & 8 deletions tests/deepmerge-custom.test.ts
Original file line number Diff line number Diff line change
@@ -8,8 +8,11 @@ import type {
DeepMergeMergeFunctionsURIs,
DeepMergeRecordsDefaultHKT,
DeepMergeLeaf,
DeepMergeOptions,
} from "@/deepmerge";

import { areAllNumbers, hasProp } from "./utils";

declare module "ava" {
interface DeepEqualAssertion {
/**
@@ -69,7 +72,8 @@ test("custom merge strings", (t) => {
declare module "../src/types" {
interface DeepMergeMergeFunctionURItoKind<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
MF extends DeepMergeMergeFunctionsURIs,
M
> {
readonly CustomArrays1: string[];
}
@@ -113,7 +117,8 @@ test("custom merge arrays", (t) => {
declare module "../src/types" {
interface DeepMergeMergeFunctionURItoKind<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
MF extends DeepMergeMergeFunctionsURIs,
M
> {
readonly CustomArrays2: Ts extends Readonly<readonly [...infer Es]>
? Es extends ReadonlyArray<unknown>
@@ -187,9 +192,10 @@ test("custom merge arrays of records", (t) => {
declare module "../src/types" {
interface DeepMergeMergeFunctionURItoKind<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
MF extends DeepMergeMergeFunctionsURIs,
M
> {
readonly CustomRecords3: Entries<DeepMergeRecordsDefaultHKT<Ts, MF>>;
readonly CustomRecords3: Entries<DeepMergeRecordsDefaultHKT<Ts, MF, M>>;
}
}

@@ -224,8 +230,10 @@ test("custom merge records", (t) => {
const customizedDeepmerge = deepmergeCustom<{
DeepMergeRecordsURI: "CustomRecords3";
}>({
mergeRecords: (records, utils) =>
Object.entries(utils.defaultMergeFunctions.mergeRecords(records, utils)),
mergeRecords: (records, utils, meta) =>
Object.entries(
utils.defaultMergeFunctions.mergeRecords(records, utils, meta)
),
});

const merged = customizedDeepmerge(x, y);
@@ -236,7 +244,8 @@ test("custom merge records", (t) => {
declare module "../src/types" {
interface DeepMergeMergeFunctionURItoKind<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
MF extends DeepMergeMergeFunctionsURIs,
M
> {
readonly NoArrayMerge1: DeepMergeLeaf<Ts>;
}
@@ -264,7 +273,8 @@ test("custom don't merge arrays", (t) => {
declare module "../src/types" {
interface DeepMergeMergeFunctionURItoKind<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs
MF extends DeepMergeMergeFunctionsURIs,
M
> {
readonly MergeDates1: EveryIsDate<Ts> extends true ? Ts : DeepMergeLeaf<Ts>;
}
@@ -300,3 +310,256 @@ test("custom merge dates", (t) => {

t.deepEqual(merged, expected);
});

test("key based merging", (t) => {
const v = { sum: 1, product: 2, mean: 3 };
const x = { sum: 4, product: 5, mean: 6 };
const y = { sum: 7, product: 8, mean: 9 };
const z = { sum: 10, product: 11, mean: 12 };

const expected = {
sum: 22,
product: 880,
mean: 7.5,
};

const customizedDeepmerge = deepmergeCustom({
mergeOthers: (values, utils, meta) => {
if (meta !== undefined && areAllNumbers(values)) {
const { key } = meta;
const numbers: ReadonlyArray<number> = values;

if (key === "sum") {
return numbers.reduce((sum, value) => sum + value);
}
if (key === "product") {
return numbers.reduce((prod, value) => prod * value);
}
if (key === "mean") {
return numbers.reduce((sum, value) => sum + value) / numbers.length;
}
}

return utils.defaultMergeFunctions.mergeOthers(values);
},
});

const merged = customizedDeepmerge(v, x, y, z);

t.deepEqual(merged, expected);
});

declare module "../src/types" {
interface DeepMergeMergeFunctionURItoKind<
Ts extends Readonly<ReadonlyArray<unknown>>,
MF extends DeepMergeMergeFunctionsURIs,
M
> {
readonly KeyPathBasedMerge: Ts[number] extends number
? Ts[number] | string
: DeepMergeLeaf<Ts>;
}
}

test("key path based merging", (t) => {
const x = {
foo: { bar: { baz: 1, qux: 2 } },
bar: { baz: 3, qux: 4 },
};
const y = {
foo: { bar: { baz: 5, bar: { baz: 6, qux: 7 } } },
bar: { baz: 8, qux: 9 },
};

const expected = {
foo: { bar: { baz: "special merge", bar: { baz: 6, qux: 7 }, qux: 2 } },
bar: { baz: "special merge", qux: 9 },
};

const customizedDeepmerge = deepmergeCustom<
{
DeepMergeOthersURI: "KeyPathBasedMerge";
},
ReadonlyArray<PropertyKey>
>({
metaDataUpdater: (previousMeta = [], metaMeta) => {
return [...previousMeta, metaMeta.key];
},
mergeOthers: (values, utils, meta) => {
if (
meta !== undefined &&
meta.length >= 2 &&
meta[meta.length - 2] === "bar" &&
meta[meta.length - 1] === "baz"
) {
return "special merge";
}

return utils.defaultMergeFunctions.mergeOthers(values);
},
});

const merged = customizedDeepmerge(x, y);

t.deepEqual(merged, expected);
});

test("key path based array merging", (t) => {
const x = {
foo: [
{ id: 1, value: ["a"] },
{ id: 2, value: ["b"] },
],
bar: [1, 2, 3],
baz: {
qux: [
{ id: 1, value: ["c"] },
{ id: 2, value: ["d"] },
],
},
qux: [
{ id: 1, value: ["e"] },
{ id: 2, value: ["f"] },
],
};
const y = {
foo: [
{ id: 2, value: ["g"] },
{ id: 1, value: ["h"] },
],
bar: [4, 5, 6],
baz: {
qux: [
{ id: 2, value: ["i"] },
{ id: 1, value: ["j"] },
],
},
qux: [
{ id: 2, value: ["k"] },
{ id: 1, value: ["l"] },
],
};

const expected = {
foo: [
{ id: 1, value: ["a", "h"] },
{ id: 2, value: ["b", "g"] },
],
bar: [1, 2, 3, 4, 5, 6],
baz: {
qux: [
{ id: 1, value: ["c", "j"] },
{ id: 2, value: ["d", "i"] },
],
},
qux: [
{ id: 1, value: ["e"] },
{ id: 2, value: ["f"] },
{ id: 2, value: ["k"] },
{ id: 1, value: ["l"] },
],
};

const customizedDeepmergeEntry = <K extends PropertyKey>(
...idsPaths: ReadonlyArray<ReadonlyArray<K>>
) => {
const mergeSettings: DeepMergeOptions<
ReadonlyArray<unknown>,
Readonly<Partial<{ id: unknown }>>
> = {
metaDataUpdater: (previousMeta = [], metaMeta) => {
return [...previousMeta, metaMeta.key ?? metaMeta.id];
},
mergeArrays: (values, utils, meta = []) => {
const idPath = idsPaths.find((idPath) => {
const parentPath = idPath.slice(0, -1);
return (
parentPath.length === meta.length &&
parentPath.every((part, i) => part === meta[i])
);
});
if (idPath === undefined) {
return utils.defaultMergeFunctions.mergeArrays(values);
}

const id = idPath[idPath.length - 1];
const valuesById = values.reduce<Map<unknown, unknown[]>>(
(carry, current) => {
const currentElementsById = new Map<unknown, unknown>();
for (const element of current) {
if (!hasProp(element, id)) {
throw new Error("Invalid element type");
}
if (currentElementsById.has(element[id])) {
throw new Error("multiple elements with the same id");
}
currentElementsById.set(element[id], element);

const currentList = carry.get(element[id]) ?? [];
carry.set(element[id], [...currentList, element]);
}
return carry;
},
new Map<unknown, unknown[]>()
);

return [...valuesById.entries()].reduce<unknown[]>(
(carry, [id, values]) => {
const childMeta = utils.metaDataUpdater(meta, { id });
return [
...carry,
deepmergeCustom(mergeSettings, childMeta)(...values),
];
},
[]
);
},
};

return deepmergeCustom(mergeSettings, []);
};

const merged = customizedDeepmergeEntry(["foo", "id"], ["baz", "qux", "id"])(
x,
y
);

t.deepEqual(merged, expected);
});

test("custom merge with parents", (t) => {
const v = { sum: 1, isBadObject: true };
const x = { sum: 2, isBadObject: false };
const y = { sum: 3, isBadObject: true };
const z = { sum: 4, isBadObject: false };

const expected = {
sum: 6,
isBadObject: false,
};

const customizedDeepmerge = deepmergeCustom({
mergeOthers: (values, utils, meta) => {
if (meta !== undefined) {
const { key, parents } = meta;
if (key === "isBadObject") {
return false;
}

const goodValues = values.filter(
(value, index): value is number =>
parents[index].isBadObject !== true && typeof value === "number"
);

if (key === "sum") {
return goodValues.reduce((sum, value) => sum + value, 0);
}
}
return utils.defaultMergeFunctions.mergeOthers(values);
},
});

const merged = customizedDeepmerge(v, x, y, z);

t.deepEqual(merged, expected);
});
12 changes: 12 additions & 0 deletions tests/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function areAllNumbers(
values: ReadonlyArray<unknown>
): values is ReadonlyArray<number> {
return values.every((value) => typeof value === "number");
}

export function hasProp<T, K extends PropertyKey>(
value: T,
prop: K
): value is T & Record<K, unknown> {
return typeof value === "object" && value !== null && prop in value;
}
31 changes: 18 additions & 13 deletions types-legacy/v4_0.d.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@
* designed to work with versions of TypeScript < 4.1
*/

declare type MetaDataUpdater = (previousMeta: any, metaMeta: any) => any;

/**
* All the options the user can pass to customize deepmerge.
*/
@@ -14,17 +16,18 @@ declare type DeepMergeOptionsFull = Readonly<{
mergeMaps: DeepMergeMergeFunctions["mergeMaps"] | false;
mergeSets: DeepMergeMergeFunctions["mergeSets"] | false;
mergeOthers: DeepMergeMergeFunctions["mergeOthers"];
metaDataUpdater: MetaDataUpdater;
}>;

/**
* All the merge functions that deepmerge uses.
*/
declare type DeepMergeMergeFunctions = Readonly<{
mergeRecords: <Ts extends Readonly<ReadonlyArray<Readonly<Record<keyof any, any>>>>, U extends DeepMergeMergeFunctionUtils>(records: Ts, utils: U) => any;
mergeArrays: <Ts extends Readonly<ReadonlyArray<Readonly<ReadonlyArray<any>>>>, U extends DeepMergeMergeFunctionUtils>(records: Ts, utils: U) => any;
mergeMaps: <Ts extends Readonly<ReadonlyArray<Readonly<ReadonlyMap<any, any>>>>, U extends DeepMergeMergeFunctionUtils>(records: Ts, utils: U) => any;
mergeSets: <Ts extends Readonly<ReadonlyArray<Readonly<ReadonlySet<any>>>>, U extends DeepMergeMergeFunctionUtils>(records: Ts, utils: U) => any;
mergeOthers: <Ts extends Readonly<ReadonlyArray<any>>, U extends DeepMergeMergeFunctionUtils>(records: Ts, utils: U) => any;
mergeRecords: <Ts extends ReadonlyArray<Readonly<Record<keyof any, any>>>, U extends DeepMergeMergeFunctionUtils>(records: Ts, utils: U, meta: any) => any;
mergeArrays: <Ts extends ReadonlyArray<ReadonlyArray<any>>, U extends DeepMergeMergeFunctionUtils>(records: Ts, utils: U, meta: any) => any;
mergeMaps: <Ts extends ReadonlyArray<Readonly<ReadonlyMap<any, any>>>, U extends DeepMergeMergeFunctionUtils>(records: Ts, utils: U, meta: any) => any;
mergeSets: <Ts extends ReadonlyArray<Readonly<ReadonlySet<any>>>, U extends DeepMergeMergeFunctionUtils>(records: Ts, utils: U, meta: any) => any;
mergeOthers: <Ts extends ReadonlyArray<any>, U extends DeepMergeMergeFunctionUtils>(records: Ts, utils: U, meta: any) => any;
}>;

/**
@@ -33,18 +36,19 @@ declare type DeepMergeMergeFunctions = Readonly<{
declare type DeepMergeMergeFunctionUtils = Readonly<{
mergeFunctions: DeepMergeMergeFunctions;
defaultMergeFunctions: DeepMergeMergeFunctionsDefaults;
deepmerge: <Ts extends Readonly<ReadonlyArray<any>>>(...values: Ts) => any;
metaDataUpdater: MetaDataUpdater;
deepmerge: <Ts extends ReadonlyArray<any>>(...values: Ts) => any;
}>;

/**
* The default merge functions.
*/
declare type DeepMergeMergeFunctionsDefaults = Readonly<{
mergeMaps: (values: Record<keyof any, any>[], utils: DeepMergeMergeFunctionUtils) => any;
mergeSets: (values: any[][], utils: DeepMergeMergeFunctionUtils) => any;
mergeArrays: (values: Set<any>[], utils: DeepMergeMergeFunctionUtils) => any;
mergeRecords: (values: Map<any, any>[], utils: DeepMergeMergeFunctionUtils) => any;
mergeOthers: (values: any[], utils: DeepMergeMergeFunctionUtils) => any;
mergeMaps: (values: Record<keyof any, any>[]) => any;
mergeSets: (values: any[][]) => any;
mergeArrays: (values: Set<any>[]) => any;
mergeRecords: (values: Map<any, any>[], utils: DeepMergeMergeFunctionUtils, meta: any) => any;
mergeOthers: (values: any[]) => any;
}>;

/**
@@ -66,10 +70,11 @@ declare function deepmerge<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>(arg0: T0, arg
declare function deepmerge(...args: any[]): any;

/**
* Deeply merge two or more objects using the given options.
* Deeply merge two or more objects using the given options and meta data.
*
* @param options - The options on how to customize the merge function.
* @param rootMetaData - The meta data passed to the root items' being merged.
*/
declare function deepmergeCustom(options: Partial<DeepMergeOptionsFull>): (...objects: any[]) => any;
declare function deepmergeCustom(options: Partial<DeepMergeOptionsFull>, rootMetaData?: any): (...objects: any[]) => any;

export { deepmerge, deepmergeCustom };

0 comments on commit 9831269

Please sign in to comment.