From 81920800d7b0aae54edad8b5d27c1c4ebf9f7584 Mon Sep 17 00:00:00 2001 From: Rebecca Stevens Date: Tue, 10 Nov 2020 14:47:14 +1300 Subject: [PATCH] fix: improve typings --- package-lock.json | 6 +- package.json | 2 +- types/index.d.ts | 179 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 161 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5ac4ded..e4ce241 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2013,9 +2013,9 @@ "dev": true }, "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "version": "4.1.1-rc", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.1-rc.tgz", + "integrity": "sha512-tgNcFrLIjlaMWEc7bKC0bxLNIt8BIAauY/HLUOQDyTP75HGskETtXOt46x4EKAHRKhWVLMc7yM02puTHa/yhCA==", "dev": true }, "uglify-js": { diff --git a/package.json b/package.json index 78e4945..d019904 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "rollup-plugin-node-resolve": "^5.2.0", "tape": "^4.11.0", "ts-node": "7.0.1", - "typescript": "^3.9.3", + "typescript": "^4.1.1-rc", "uglify-js": "^3.6.1" }, "license": "MIT", diff --git a/types/index.d.ts b/types/index.d.ts index 7bf9cde..34aa7e2 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,32 +1,167 @@ +// Minimum TypeScript Version: 4.2 +// TODO: Can be reduce to version 4.1 upon it's release. + +type DeepMergeAll< + Ts extends readonly [any, ...any[]], + Options extends deepmerge.Options +> = Ts extends readonly [infer T1, ...any[]] + ? Ts extends readonly [any, infer T2, ...infer TRest] + ? TRest extends readonly never[] + ? DeepMerge + : DeepMerge, Options> + : T1 + : never; + +type DeepMerge = IsSame< + T1, + T2 +> extends true + ? T1 | T2 + : IsObjectOrArray extends true + ? IsObjectOrArray extends true + ? DeepMergeNonPrimitive + : MergeLeafs + : MergeLeafs; + +type DeepMergeNonPrimitive< + T1, + T2, + Options extends deepmerge.Options +> = ShouldMergeArrays extends true + ? DeepMergeArrays + : DeepMergeObjects; + +type DeepMergeObjects< + T1, + T2, + Options extends deepmerge.Options +> = ShouldMergeObjects extends true + ? DeepMergeObjectProps + : MergeLeafs; + +// @see https://github.com/microsoft/TypeScript/issues/41448 +type DeepMergeObjectProps< + T1, + T2, + Options extends deepmerge.Options +> = FlatternAlias< + { + -readonly [K in keyof T1]: Options["customMerge"] extends undefined + ? DeepMerge, ValueOfKey, Options> + : ReturnType> extends undefined + ? DeepMerge, ValueOfKey, Options> + : ReturnType< + NonNullable>> + >; + } & + { + -readonly [K in keyof T2]: Options["customMerge"] extends undefined + ? DeepMerge, ValueOfKey, Options> + : ReturnType> extends undefined + ? DeepMerge, ValueOfKey, Options> + : ReturnType< + NonNullable>> + >; + } +>; + +type DeepMergeArrays< + T1, + T2, + Options extends deepmerge.Options +> = T1 extends readonly [...infer E1] + ? T2 extends readonly [...infer E2] + ? Options["arrayMerge"] extends undefined + ? [...E1, ...E2] + : ReturnType> + : never + : never; + +type MergeLeafs = Is extends true + ? T1 + : Is extends true + ? T2 + : Is extends true + ? T1 + : T2; + +type FlatternAlias = T extends any + ? { + [K in keyof T]: T[K]; + } + : never; + type ValueOfKey = K extends keyof T ? T[K] : never; -// Is object AND NOT ('never' or 'array') -type IsObject = [T] extends [never] ? false : T extends unknown[] ? false : [T] extends [object] ? true : false; -type ShouldMergeProps = IsObject extends false ? false : IsObject extends false ? false : true; -type DeepMerge = ShouldMergeProps extends true - ? { - [K in keyof T1 | keyof T2]: DeepMerge, ValueOfKey>; - } - : T1 | T2; + +type Is = [T1] extends [T2] ? true : false; + +type IsSame = Is extends true ? Is : false; + +type IsObjectOrArray = Is extends true ? false : Is; + +type IsObject = Is> extends true + ? false + : IsObjectOrArray; + +type ShouldMergeObjects = IsObject extends true + ? IsObject + : false; + +type ShouldMergeArrays = Is> extends true + ? Is> + : false; + +type ArrayMerge = ( + target: Array, + source: Array, + options: Required +) => any; + +type ObjectMerge = ( + key: string, + options: Required +) => + | ((target: any, source: any, options?: deepmerge.Options) => any) + | undefined; + +type IsMergeable = (value: any) => boolean; + +type DefaultOptions = { + arrayMerge: undefined; + clone: true; + customMerge: undefined; + isMergeableObject: undefined; +}; /** -* ## JS Object Deep Merge -* look more info in https://github.com/TehShrike/deepmerge -* @param x the first obj -* @param y the second obj -* @param options deepmerge option -*/ -declare function deepmerge(x: T1, y: T2, options?: deepmerge.Options): DeepMerge + * Deeply merge two objects. + * + * @param target the first obj + * @param source the second obj + * @param options deepmerge option + */ +declare function deepmerge< + T1 extends object, + T2 extends object, + Options extends deepmerge.Options = DefaultOptions +>(target: T1, source: T2, options?: Options): DeepMerge; declare namespace deepmerge { - export interface Options { - arrayMerge?(target: any[], source: any[], options?: Options): any[]; + export type Options = { + arrayMerge?: ArrayMerge; clone?: boolean; - customMerge?: (key: string, options?: Options) => ((x: any, y: any) => any) | undefined; - isMergeableObject?(value: object): boolean; - } + customMerge?: ObjectMerge; + isMergeableObject?: IsMergeable; + }; - export function all (objects: object[], options?: Options): object; - export function all (objects: Partial[], options?: Options): T; + export function all< + Ts extends readonly [object, ...object[]], + O extends Options = DefaultOptions + >(objects: [...Ts], options?: O): DeepMergeAll; + export function all( + objects: ReadonlyArray, + options?: Options + ): object; } export = deepmerge;