Skip to content

Commit

Permalink
fix: add better support for readonly types (#17)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: interface DeepMergeMergeFunctionURItoKind's signature has changed
  • Loading branch information
RebeccaStevens committed Nov 22, 2021
1 parent 361394e commit ee59064
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 174 deletions.
11 changes: 5 additions & 6 deletions docs/deepmergeCustom.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 Readonly<ReadonlyArray<unknown>>, MF extends DeepMergeMergeFunctionsURIs> {
readonly MyCustomMergeURI: MyValue;
}
}
Expand Down Expand Up @@ -70,17 +70,16 @@ customDeepmerge(x, y, z); // => { foo: [Date, Date, Date] }

declare module "deepmerge-ts" {
interface DeepMergeMergeFunctionURItoKind<
Ts extends ReadonlyArray<unknown>,
Ts extends Readonly<ReadonlyArray<unknown>>,
MF extends DeepMergeMergeFunctionsURIs
> {
readonly MyDeepMergeDatesURI: EveryIsDate<Ts> extends true ? Ts : DeepMergeLeaf<Ts>;
}
}

type EveryIsDate<Ts extends ReadonlyArray<unknown>> = Ts extends readonly [
infer Head,
...infer Rest
]
type EveryIsDate<Ts extends Readonly<ReadonlyArray<unknown>>> = Ts extends Readonly<
readonly [infer Head, ...infer Rest]
>
? Head extends Date
? EveryIsDate<Rest>
: false
Expand Down
34 changes: 19 additions & 15 deletions src/deepmerge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export type DeepMergeMergeFunctionsDefaults = typeof defaultOptions;
*
* @param objects - The objects to merge.
*/
export function deepmerge<Ts extends ReadonlyArray<unknown>>(
...objects: readonly [...Ts]
export function deepmerge<Ts extends Readonly<ReadonlyArray<unknown>>>(
...objects: Readonly<readonly [...Ts]>
): DeepMergeHKT<Ts, DeepMergeMergeFunctionsDefaultURIs> {
return deepmergeCustom({})(...objects) as DeepMergeHKT<
Ts,
Expand All @@ -54,13 +54,13 @@ export function deepmergeCustom<
PMF extends Partial<DeepMergeMergeFunctionsURIs>
>(
options: DeepMergeOptions
): <Ts extends ReadonlyArray<unknown>>(
): <Ts extends Readonly<ReadonlyArray<unknown>>>(
...objects: Ts
) => DeepMergeHKT<Ts, GetDeepMergeMergeFunctionsURIs<PMF>> {
/**
* The type of the customized deepmerge function.
*/
type CustomizedDeepmerge = <Ts extends ReadonlyArray<unknown>>(
type CustomizedDeepmerge = <Ts extends Readonly<ReadonlyArray<unknown>>>(
...objects: Ts
) => DeepMergeHKT<Ts, GetDeepMergeMergeFunctionsURIs<PMF>>;

Expand All @@ -69,7 +69,7 @@ export function deepmergeCustom<
/**
* The customized deepmerge function.
*/
function customizedDeepmerge(...objects: ReadonlyArray<unknown>) {
function customizedDeepmerge(...objects: Readonly<ReadonlyArray<unknown>>) {
if (objects.length === 0) {
return undefined;
}
Expand Down Expand Up @@ -112,7 +112,7 @@ function getUtils(
* @param values - The values.
*/
function mergeUnknowns<
Ts extends ReadonlyArray<unknown>,
Ts extends Readonly<ReadonlyArray<unknown>>,
U extends DeepMergeMergeFunctionUtils,
MF extends DeepMergeMergeFunctionsURIs
>(values: Ts, utils: U): DeepMergeHKT<Ts, MF> {
Expand All @@ -136,25 +136,29 @@ function mergeUnknowns<
switch (type) {
case ObjectType.RECORD:
return utils.mergeFunctions.mergeRecords(
values as ReadonlyArray<Readonly<Record<PropertyKey, unknown>>>,
values as Readonly<
ReadonlyArray<Readonly<Record<PropertyKey, unknown>>>
>,
utils
) as DeepMergeHKT<Ts, MF>;

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

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

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

Expand All @@ -172,7 +176,7 @@ function mergeUnknowns<
* @param values - The records.
*/
function mergeRecords<
Ts extends ReadonlyArray<Record<PropertyKey, unknown>>,
Ts extends Readonly<ReadonlyArray<Record<PropertyKey, unknown>>>,
U extends DeepMergeMergeFunctionUtils,
MF extends DeepMergeMergeFunctionsURIs
>(values: Ts, utils: U) {
Expand Down Expand Up @@ -208,7 +212,7 @@ function mergeRecords<
* @param values - The arrays.
*/
function mergeArrays<
Ts extends ReadonlyArray<ReadonlyArray<unknown>>,
Ts extends Readonly<ReadonlyArray<Readonly<ReadonlyArray<unknown>>>>,
U extends DeepMergeMergeFunctionUtils,
MF extends DeepMergeMergeFunctionsURIs
>(values: Ts, utils: U) {
Expand All @@ -221,7 +225,7 @@ function mergeArrays<
* @param values - The sets.
*/
function mergeSets<
Ts extends ReadonlyArray<Readonly<ReadonlySet<unknown>>>,
Ts extends Readonly<ReadonlyArray<Readonly<ReadonlySet<unknown>>>>,
U extends DeepMergeMergeFunctionUtils,
MF extends DeepMergeMergeFunctionsURIs
>(values: Ts, utils: U) {
Expand All @@ -237,7 +241,7 @@ function mergeSets<
* @param values - The maps.
*/
function mergeMaps<
Ts extends ReadonlyArray<Readonly<ReadonlyMap<unknown, unknown>>>,
Ts extends Readonly<ReadonlyArray<Readonly<ReadonlyMap<unknown, unknown>>>>,
U extends DeepMergeMergeFunctionUtils,
MF extends DeepMergeMergeFunctionsURIs
>(values: Ts, utils: U) {
Expand All @@ -253,7 +257,7 @@ function mergeMaps<
* @param values - The values.
*/
function leaf<
Ts extends ReadonlyArray<unknown>,
Ts extends Readonly<ReadonlyArray<unknown>>,
U extends DeepMergeMergeFunctionUtils,
MF extends DeepMergeMergeFunctionsURIs
>(values: Ts, utils: U) {
Expand Down
48 changes: 27 additions & 21 deletions src/types/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ type DeepMergeMapsDefaultURI = "DeepMergeMapsDefaultURI";
/**
* The default merge functions to use when deep merging.
*/
export type DeepMergeMergeFunctionsDefaultURIs = {
export type DeepMergeMergeFunctionsDefaultURIs = Readonly<{
DeepMergeRecordsURI: DeepMergeRecordsDefaultURI;
DeepMergeArraysURI: DeepMergeArraysDefaultURI;
DeepMergeSetsURI: DeepMergeSetsDefaultURI;
DeepMergeMapsURI: DeepMergeMapsDefaultURI;
DeepMergeOthersURI: DeepMergeLeafURI;
};
}>;

/**
* A union of all the props that should not be included in type information for
Expand All @@ -56,9 +56,9 @@ type BlacklistedRecordProps = "__proto__";
* Deep merge records.
*/
export type DeepMergeRecordsDefaultHKT<
Ts extends ReadonlyArray<unknown>,
Ts extends Readonly<ReadonlyArray<unknown>>,
MF extends DeepMergeMergeFunctionsURIs
> = Ts extends readonly [unknown, ...unknown[]]
> = Ts extends Readonly<readonly [unknown, ...Readonly<ReadonlyArray<unknown>>]>
? FlatternAlias<
Omit<
DeepMergeRecordsDefaultHKTInternalProps<Ts, MF>,
Expand All @@ -71,7 +71,7 @@ export type DeepMergeRecordsDefaultHKT<
* Deep merge record props.
*/
type DeepMergeRecordsDefaultHKTInternalProps<
Ts extends readonly [unknown, ...unknown[]],
Ts extends Readonly<readonly [unknown, ...Readonly<ReadonlyArray<unknown>>]>,
MF extends DeepMergeMergeFunctionsURIs
> = {
[K in OptionalKeysOf<Ts>]?: DeepMergeHKT<
Expand All @@ -89,22 +89,28 @@ type DeepMergeRecordsDefaultHKTInternalProps<
* Get the value of the property.
*/
type DeepMergeRecordsDefaultHKTInternalPropValue<
Ts extends readonly [unknown, ...unknown[]],
Ts extends Readonly<readonly [unknown, ...Readonly<ReadonlyArray<unknown>>]>,
K extends PropertyKey
> = FilterOutNever<
DeepMergeRecordsDefaultHKTInternalPropValueHelper<Ts, K, []>
DeepMergeRecordsDefaultHKTInternalPropValueHelper<
Ts,
K,
Readonly<readonly []>
>
>;

/**
* Tail-recursive helper type for DeepMergeRecordsDefaultHKTInternalPropValue.
*/
type DeepMergeRecordsDefaultHKTInternalPropValueHelper<
Ts extends readonly [unknown, ...unknown[]],
Ts extends Readonly<readonly [unknown, ...Readonly<ReadonlyArray<unknown>>]>,
K extends PropertyKey,
Acc extends ReadonlyArray<unknown>
> = Ts extends readonly [infer Head, ...infer Rest]
Acc extends Readonly<ReadonlyArray<unknown>>
> = Ts extends Readonly<readonly [infer Head, ...infer Rest]>
? Head extends Record<PropertyKey, unknown>
? Rest extends readonly [unknown, ...unknown[]]
? Rest extends Readonly<
readonly [unknown, ...Readonly<ReadonlyArray<unknown>>]
>
? DeepMergeRecordsDefaultHKTInternalPropValueHelper<
Rest,
K,
Expand All @@ -118,22 +124,22 @@ type DeepMergeRecordsDefaultHKTInternalPropValueHelper<
* Deep merge 2 arrays.
*/
export type DeepMergeArraysDefaultHKT<
Ts extends ReadonlyArray<unknown>,
Ts extends Readonly<ReadonlyArray<unknown>>,
MF extends DeepMergeMergeFunctionsURIs
> = DeepMergeArraysDefaultHKTHelper<Ts, MF, []>;

/**
* Tail-recursive helper type for DeepMergeArraysDefaultHKT.
*/
type DeepMergeArraysDefaultHKTHelper<
Ts extends ReadonlyArray<unknown>,
Ts extends Readonly<ReadonlyArray<unknown>>,
MF extends DeepMergeMergeFunctionsURIs,
Acc extends ReadonlyArray<unknown>
Acc extends Readonly<ReadonlyArray<unknown>>
> = Ts extends readonly [infer Head, ...infer Rest]
? Head extends ReadonlyArray<unknown>
? Head extends Readonly<ReadonlyArray<unknown>>
? Rest extends readonly [
ReadonlyArray<unknown>,
...ReadonlyArray<ReadonlyArray<unknown>>
Readonly<ReadonlyArray<unknown>>,
...Readonly<ReadonlyArray<Readonly<ReadonlyArray<unknown>>>>
]
? DeepMergeArraysDefaultHKTHelper<Rest, MF, [...Acc, ...Head]>
: [...Acc, ...Head]
Expand All @@ -144,15 +150,15 @@ type DeepMergeArraysDefaultHKTHelper<
* Deep merge 2 sets.
*/
export type DeepMergeSetsDefaultHKT<
Ts extends ReadonlyArray<unknown>,
Ts extends Readonly<ReadonlyArray<unknown>>,
MF extends DeepMergeMergeFunctionsURIs
> = Set<UnionSetValues<Ts>>;

/**
* Deep merge 2 maps.
*/
export type DeepMergeMapsDefaultHKT<
Ts extends ReadonlyArray<unknown>,
Ts extends Readonly<ReadonlyArray<unknown>>,
MF extends DeepMergeMergeFunctionsURIs
> = Map<UnionMapKeys<Ts>, UnionMapValues<Ts>>;

Expand All @@ -161,7 +167,7 @@ export type DeepMergeMapsDefaultHKT<
*/
export type GetDeepMergeMergeFunctionsURIs<
PMF extends Partial<DeepMergeMergeFunctionsURIs>
> = {
> = Readonly<{
// prettier-ignore
DeepMergeRecordsURI:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -196,4 +202,4 @@ export type GetDeepMergeMergeFunctionsURIs<
PMF["DeepMergeOthersURI"] extends keyof DeepMergeMergeFunctionURItoKind<any, any>
? PMF["DeepMergeOthersURI"]
: DeepMergeLeafURI;
};
}>;

0 comments on commit ee59064

Please sign in to comment.