Skip to content

Commit

Permalink
feat: allow for default merging via a special return value
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaStevens committed Feb 24, 2022
1 parent 1d5e617 commit 658d1fd
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 20 deletions.
25 changes: 21 additions & 4 deletions docs/deepmergeCustom.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,10 @@ declare module "../src/types" {
}
```

## Implicit Default Merging
## Default Merging

If you do not want to have to explicitly perform default merging in your custom merge functions;
you can set the option `enableImplicitDefaultMerging` to `true`. Once set, if any of your custom
merge functions return `undefined`, then the default merging functions will automatically be called.
If you do not want to have to explicitly call the default merging function in your custom merge function;
you can just return `utils.actions.defaultMerge`. This will automatically apply the default merging strategy.

For example, the following `customizedDeepmerge` functions are equivalent:

Expand All @@ -221,6 +220,24 @@ const customizedDeepmerge = deepmergeCustom({
});
```

```ts
const customizedDeepmerge = deepmergeCustom({
mergeOthers: (value, utils) => {
if (someCondition) {
return someCustomValue;
}
return utils.actions.defaultMerge;
},
});
```

### Implicit

You can also set the option `enableImplicitDefaultMerging` to `true` to make it so that if any of your
custom merge functions return `undefined`, then the default merging strategy will automatically be applied.

For example, the following `customizedDeepmerge` function is equivalent to the two above:

```ts
const customizedDeepmerge = deepmergeCustom({
enableImplicitDefaultMerging: true, // enable implicit default merging
Expand Down
47 changes: 31 additions & 16 deletions src/deepmerge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ const defaultMergeFunctions = {
mergeOthers: leaf,
} as const;

/**
* Special values that tell deepmerge-ts to perform a certain action.
*/
const actions = {
defaultMerge: Symbol("deepmerge-ts: default merge"),
} as const;

/**
* The default function to update meta data.
*/
Expand Down Expand Up @@ -163,6 +170,7 @@ function getUtils<M, MM extends DeepMergeBuiltInMetaData>(
>["metaDataUpdater"],
deepmerge: customizedDeepmerge,
useImplicitDefaultMerging: options.enableImplicitDefaultMerging ?? false,
actions,
};
}

Expand Down Expand Up @@ -263,10 +271,11 @@ function mergeRecords<
const result = utils.mergeFunctions.mergeRecords(values, utils, meta);

if (
utils.useImplicitDefaultMerging &&
result === undefined &&
utils.mergeFunctions.mergeRecords !==
utils.defaultMergeFunctions.mergeRecords
result === actions.defaultMerge ||
(utils.useImplicitDefaultMerging &&
result === undefined &&
utils.mergeFunctions.mergeRecords !==
utils.defaultMergeFunctions.mergeRecords)
) {
return utils.defaultMergeFunctions.mergeRecords<
ReadonlyArray<Readonly<Record<PropertyKey, unknown>>>,
Expand Down Expand Up @@ -297,9 +306,11 @@ function mergeArrays<
const result = utils.mergeFunctions.mergeArrays(values, utils, meta);

if (
utils.useImplicitDefaultMerging &&
result === undefined &&
utils.mergeFunctions.mergeArrays !== utils.defaultMergeFunctions.mergeArrays
result === actions.defaultMerge ||
(utils.useImplicitDefaultMerging &&
result === undefined &&
utils.mergeFunctions.mergeArrays !==
utils.defaultMergeFunctions.mergeArrays)
) {
return utils.defaultMergeFunctions.mergeArrays(values);
}
Expand All @@ -323,9 +334,10 @@ function mergeSets<
const result = utils.mergeFunctions.mergeSets(values, utils, meta);

if (
utils.useImplicitDefaultMerging &&
result === undefined &&
utils.mergeFunctions.mergeSets !== utils.defaultMergeFunctions.mergeSets
result === actions.defaultMerge ||
(utils.useImplicitDefaultMerging &&
result === undefined &&
utils.mergeFunctions.mergeSets !== utils.defaultMergeFunctions.mergeSets)
) {
return utils.defaultMergeFunctions.mergeSets(values);
}
Expand All @@ -349,9 +361,10 @@ function mergeMaps<
const result = utils.mergeFunctions.mergeMaps(values, utils, meta);

if (
utils.useImplicitDefaultMerging &&
result === undefined &&
utils.mergeFunctions.mergeMaps !== utils.defaultMergeFunctions.mergeMaps
result === actions.defaultMerge ||
(utils.useImplicitDefaultMerging &&
result === undefined &&
utils.mergeFunctions.mergeMaps !== utils.defaultMergeFunctions.mergeMaps)
) {
return utils.defaultMergeFunctions.mergeMaps(values);
}
Expand All @@ -371,9 +384,11 @@ function mergeOthers<
const result = utils.mergeFunctions.mergeOthers(values, utils, meta);

if (
utils.useImplicitDefaultMerging &&
result === undefined &&
utils.mergeFunctions.mergeOthers !== utils.defaultMergeFunctions.mergeOthers
result === actions.defaultMerge ||
(utils.useImplicitDefaultMerging &&
result === undefined &&
utils.mergeFunctions.mergeOthers !==
utils.defaultMergeFunctions.mergeOthers)
) {
return utils.defaultMergeFunctions.mergeOthers(values);
}
Expand Down
3 changes: 3 additions & 0 deletions src/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,7 @@ export type DeepMergeMergeFunctionUtils<
metaDataUpdater: MetaDataUpdater<M, MM>;
deepmerge: <Ts extends ReadonlyArray<unknown>>(...values: Ts) => unknown;
useImplicitDefaultMerging: boolean;
actions: Readonly<{
defaultMerge: symbol;
}>;
}>;
35 changes: 35 additions & 0 deletions tests/deepmerge-custom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -629,3 +629,38 @@ test("implicit default merging", (t) => {

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

test("default merging using shortcut", (t) => {
const x = {
foo: 1,
bar: { baz: [2], qux: new Set([1]), quux: new Map([[1, 2]]) },
};
const y = {
foo: 3,
bar: { baz: [4], qux: new Set([2]), quux: new Map([[2, 3]]) },
};

const expected = {
foo: 3,
bar: {
baz: [2, 4],
qux: new Set([1, 2]),
quux: new Map([
[1, 2],
[2, 3],
]),
},
};

const customizedDeepmerge = deepmergeCustom({
mergeRecords: (value, utils) => utils.actions.defaultMerge,
mergeArrays: (value, utils) => utils.actions.defaultMerge,
mergeSets: (value, utils) => utils.actions.defaultMerge,
mergeMaps: (value, utils) => utils.actions.defaultMerge,
mergeOthers: (value, utils) => utils.actions.defaultMerge,
});

const merged = customizedDeepmerge(x, y);

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

0 comments on commit 658d1fd

Please sign in to comment.