From 813fa390cebdc3c9da8826c8118985d695cc11eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zieli=C5=84ski?= Date: Tue, 7 Jun 2022 16:54:29 +0200 Subject: [PATCH 1/3] Add SelectorWithCustomCurrySignature, a utility type to provide a custom curry signature for selectors when TypeScript inference falls short. --- packages/data/src/types.ts | 65 +++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index a43f8e7f27250..ebd0dfa5c1b0d 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -76,18 +76,73 @@ type Store< : never; /** - * Removes the first argument from a function + * Removes the first argument from a function. * - * This is designed to remove the `state` parameter from + * By default, it removes the `state` parameter from * registered selectors since that argument is supplied * by the editor when calling `select(…)`. * * For functions with no arguments, which some selectors * are free to define, returns the original function. + * + * It is possible to manually provide a custom curried signature + * and avoid the automatic inference. When the + * F generic argument passed to this helper extends the + * SelectorWithCustomCurrySignature type, the F['CurriedSignature'] + * property is used verbatim. + * + * This is useful because TypeScript does not correctly remove + * arguments from complex function signatures constrained by + * interdependent generic parameters. + */ +type CurriedState< F > = + F extends SelectorWithCustomCurrySignature + ? F['CurriedSignature'] + : F extends ( state: any, ...args: infer P ) => infer R + ? ( ...args: P ) => R + : F; + +/** + * Utility to manually specify curried selector signatures. + * + * It comes handy when TypeScript can't automatically produce the + * correct curried function signature. For example: + * + * ```ts + * type BadlyInferredSignature = CurriedState< + * ( + * state: any, + * kind: K, + * key: K extends string ? 'one value' : false + * ) => K + * > + * // BadlyInferredSignature evaluates to: + * // (kind: string number, key: false "one value") => string number + * ``` + * + * With SelectorWithCustomCurrySignature, we can provide a custom + * signature and avoid relying on TypeScript inference: + * ```ts + * interface MySelectorSignature extends SelectorWithCustomCurrySignature { + * ( + * state: any, + * kind: K, + * key: K extends string ? 'one value' : false + * ): K; + * + * CurriedSignature: ( + * kind: K, + * key: K extends string ? 'one value' : false + * ): K; + * } + * type CorrectlyInferredSignature = CurriedState + * // (kind: K, key: K extends string ? 'one value' : false): K; + * ``` */ -type CurriedState< F > = F extends ( state: any, ...args: infer P ) => infer R - ? ( ...args: P ) => R - : F; +export interface SelectorWithCustomCurrySignature { + __isCurryContainer: true; + CurriedSignature: Function; +} /** * Returns a function whose return type is a Promise of the given return type. From d1c09248c951e2f73fb133d408d97d27c0b571cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zieli=C5=84ski?= Date: Tue, 7 Jun 2022 17:22:44 +0200 Subject: [PATCH 2/3] Add the PR link to the docstrings --- packages/data/src/types.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index ebd0dfa5c1b0d..64dedeafbb192 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -94,6 +94,7 @@ type Store< * This is useful because TypeScript does not correctly remove * arguments from complex function signatures constrained by * interdependent generic parameters. + * For more context, see https://github.com/WordPress/gutenberg/pull/41578 */ type CurriedState< F > = F extends SelectorWithCustomCurrySignature @@ -137,6 +138,8 @@ type CurriedState< F > = * } * type CorrectlyInferredSignature = CurriedState * // (kind: K, key: K extends string ? 'one value' : false): K; + * + * For even more context, see https://github.com/WordPress/gutenberg/pull/41578 * ``` */ export interface SelectorWithCustomCurrySignature { From a37d2a2af9dd0de9f03dfe65eb7c4004b581815a Mon Sep 17 00:00:00 2001 From: Adam Zielinski Date: Thu, 7 Jul 2022 14:00:03 +0200 Subject: [PATCH 3/3] Update packages/data/src/types.ts --- packages/data/src/types.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index 64dedeafbb192..c8392f42a1707 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -96,13 +96,11 @@ type Store< * interdependent generic parameters. * For more context, see https://github.com/WordPress/gutenberg/pull/41578 */ -type CurriedState< F > = - F extends SelectorWithCustomCurrySignature - ? F['CurriedSignature'] - : F extends ( state: any, ...args: infer P ) => infer R - ? ( ...args: P ) => R - : F; - +type CurriedState< F > = F extends SelectorWithCustomCurrySignature + ? F[ 'CurriedSignature' ] + : F extends ( state: any, ...args: infer P ) => infer R + ? ( ...args: P ) => R + : F; /** * Utility to manually specify curried selector signatures. *