From 4401ac2d1651093ab855d3d4bdf6c9628c0767ab Mon Sep 17 00:00:00 2001 From: Rebecca Stevens Date: Tue, 23 Nov 2021 00:49:30 +1300 Subject: [PATCH] perf: convert recursive types to tail-recursive versions (#15) This gives performance improvements for types with TypeScript 4.5. --- src/types/defaults.ts | 73 ++++++++++---------- src/types/utils.ts | 151 ++++++++++++++++++++++++++++-------------- 2 files changed, 137 insertions(+), 87 deletions(-) diff --git a/src/types/defaults.ts b/src/types/defaults.ts index 5761174e..959306e5 100644 --- a/src/types/defaults.ts +++ b/src/types/defaults.ts @@ -76,57 +76,42 @@ type DeepMergeRecordsDefaultHKTInternalProps< MF extends DeepMergeMergeFunctionsURIs > = { [K in OptionalKeysOf]?: DeepMergeHKT< - FilterOutNever< - DeepMergeRecordsDefaultHKTInternalPropsToMerge< - DeepMergeRecordsDefaultHKTInternalPropValue - > - >, + DeepMergeRecordsDefaultHKTInternalPropValue, MF >; } & { [K in RequiredKeysOf]: DeepMergeHKT< - FilterOutNever< - DeepMergeRecordsDefaultHKTInternalPropsToMerge< - DeepMergeRecordsDefaultHKTInternalPropValue - > - >, + DeepMergeRecordsDefaultHKTInternalPropValue, MF >; }; -/** - * Get the properties to merge. - */ -type DeepMergeRecordsDefaultHKTInternalPropsToMerge< - Ts extends readonly [unknown, unknown] -> = Ts extends readonly [infer First, infer Second] - ? IsNever extends true - ? Second extends readonly [unknown, unknown] - ? DeepMergeRecordsDefaultHKTInternalPropsToMerge - : Second extends readonly [unknown] - ? Second - : [] - : Second extends readonly [unknown, unknown] - ? [First, ...DeepMergeRecordsDefaultHKTInternalPropsToMerge] - : Second extends readonly [unknown] - ? [First, Second[0]] - : [] - : never; - /** * Get the value of the property. */ type DeepMergeRecordsDefaultHKTInternalPropValue< Ts extends readonly [unknown, ...unknown[]], K extends PropertyKey +> = FilterOutNever< + DeepMergeRecordsDefaultHKTInternalPropValueHelper +>; + +/** + * Tail-recursive helper type for DeepMergeRecordsDefaultHKTInternalPropValue. + */ +type DeepMergeRecordsDefaultHKTInternalPropValueHelper< + Ts extends readonly [unknown, ...unknown[]], + K extends PropertyKey, + Acc extends ReadonlyArray > = Ts extends readonly [infer Head, ...infer Rest] ? Head extends Record ? Rest extends readonly [unknown, ...unknown[]] - ? [ - ValueOfKey, - DeepMergeRecordsDefaultHKTInternalPropValue - ] - : [ValueOfKey] + ? DeepMergeRecordsDefaultHKTInternalPropValueHelper< + Rest, + K, + [...Acc, ValueOfKey] + > + : [...Acc, ValueOfKey] : never : never; @@ -136,11 +121,23 @@ type DeepMergeRecordsDefaultHKTInternalPropValue< export type DeepMergeArraysDefaultHKT< Ts extends ReadonlyArray, MF extends DeepMergeMergeFunctionsURIs -> = Ts extends [infer Head, ...infer Rest] +> = DeepMergeArraysDefaultHKTHelper; + +/** + * Tail-recursive helper type for DeepMergeArraysDefaultHKT. + */ +type DeepMergeArraysDefaultHKTHelper< + Ts extends ReadonlyArray, + MF extends DeepMergeMergeFunctionsURIs, + Acc extends ReadonlyArray +> = Ts extends readonly [infer Head, ...infer Rest] ? Head extends ReadonlyArray - ? Rest extends [ReadonlyArray, ...ReadonlyArray] - ? [...Head, ...DeepMergeArraysDefaultHKT] - : Head + ? Rest extends readonly [ + ReadonlyArray, + ...ReadonlyArray + ] + ? DeepMergeArraysDefaultHKTHelper + : [...Acc, ...Head] : never : never; diff --git a/src/types/utils.ts b/src/types/utils.ts index 096f441a..c12e0cbb 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -156,51 +156,80 @@ export type Not = T extends true ? false : true; * Union of the sets' values' types */ export type UnionSetValues> = - Ts extends readonly [infer Head, ...infer Rest] - ? Head extends Set - ? Rest extends ReadonlyArray - ? UnionSetValues | V1 - : V1 - : never - : never; + UnionSetValuesHelper; + +/** + * Tail-recursive helper type for UnionSetValues. + */ +type UnionSetValuesHelper< + Ts extends ReadonlyArray, + Acc +> = Ts extends readonly [infer Head, ...infer Rest] + ? Head extends Set + ? Rest extends ReadonlyArray + ? UnionSetValuesHelper + : Acc | V1 + : never + : Acc; /** * Union of the maps' values' types */ export type UnionMapKeys> = - Ts extends readonly [infer Head, ...infer Rest] - ? Head extends Map - ? Rest extends readonly [] - ? K1 - : K1 | UnionMapKeys - : never - : never; + UnionMapKeysHelper; + +/** + * Tail-recursive helper type for UnionMapKeys. + */ +type UnionMapKeysHelper< + Ts extends ReadonlyArray, + Acc +> = Ts extends readonly [infer Head, ...infer Rest] + ? Head extends Map + ? Rest extends readonly [] + ? Acc | K1 + : UnionMapKeysHelper + : never + : Acc; /** * Union of the maps' keys' types */ export type UnionMapValues> = - Ts extends readonly [infer Head, ...infer Rest] - ? Head extends Map - ? Rest extends readonly [] - ? V1 - : UnionMapValues | V1 - : never - : never; + UnionMapValuesHelper; + +/** + * Tail-recursive helper type for UnionMapValues. + */ +type UnionMapValuesHelper< + Ts extends ReadonlyArray, + Acc +> = Ts extends readonly [infer Head, ...infer Rest] + ? Head extends Map + ? Rest extends readonly [] + ? Acc | V1 + : UnionMapValuesHelper + : never + : Acc; /** * Get all the keys of the given records. */ -export type KeysOf> = Ts extends readonly [ - infer Head, - ...infer Rest -] +export type KeysOf> = KeysOfHelper; + +/** + * Tail-recursive helper type for KeysOf. + */ +type KeysOfHelper< + Ts extends ReadonlyArray, + Acc +> = Ts extends readonly [infer Head, ...infer Rest] ? Head extends Record ? Rest extends ReadonlyArray - ? KeysOf | keyof Head - : keyof Head + ? KeysOfHelper + : Acc | keyof Head : never - : never; + : Acc; /** * Get the keys of the type what match a certain criteria. @@ -221,13 +250,21 @@ type RequiredKeys = Exclude< * Get all the required keys on the types in the tuple. */ export type RequiredKeysOf = - Ts extends readonly [infer Head, ...infer Rest] - ? Head extends Record - ? Rest extends readonly [unknown, ...unknown[]] - ? RequiredKeys | RequiredKeysOf - : RequiredKeys - : never - : never; + RequiredKeysOfHelper; + +/** + * Tail-recursive helper type for RequiredKeysOf. + */ +type RequiredKeysOfHelper< + Ts extends readonly [unknown, ...unknown[]], + Acc +> = Ts extends readonly [infer Head, ...infer Rest] + ? Head extends Record + ? Rest extends readonly [unknown, ...unknown[]] + ? RequiredKeysOfHelper> + : Acc | RequiredKeys + : never + : Acc; /** * Get the optional keys of the type. @@ -238,25 +275,41 @@ type OptionalKeys = Exclude>; * Get all the optional keys on the types in the tuple. */ export type OptionalKeysOf = - Ts extends readonly [infer Head, ...infer Rest] - ? Head extends Record - ? Rest extends readonly [unknown, ...unknown[]] - ? OptionalKeys | OptionalKeysOf - : OptionalKeys - : never - : never; + OptionalKeysOfHelper; + +/** + * Tail-recursive helper type for OptionalKeysOf. + */ +type OptionalKeysOfHelper< + Ts extends readonly [unknown, ...unknown[]], + Acc +> = Ts extends readonly [infer Head, ...infer Rest] + ? Head extends Record + ? Rest extends readonly [unknown, ...unknown[]] + ? OptionalKeysOfHelper> + : Acc | OptionalKeys + : never + : Acc; /** * Filter out nevers from a tuple. */ export type FilterOutNever> = - T extends readonly [] - ? [] - : T extends [infer Head, ...infer Rest] - ? IsNever extends true - ? FilterOutNever - : [Head, ...FilterOutNever] - : T; + FilterOutNeverHelper; + +/** + * Tail-recursive helper type for FilterOutNever. + */ +type FilterOutNeverHelper< + T extends ReadonlyArray, + Acc extends ReadonlyArray +> = T extends readonly [] + ? Acc + : T extends readonly [infer Head, ...infer Rest] + ? IsNever extends true + ? FilterOutNeverHelper + : FilterOutNeverHelper + : T; /** * Is the type a tuple?