From 5162962a5c1f7f17c4f2f19d2bda051353839a60 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 11 Jul 2023 13:51:30 -0700 Subject: [PATCH 01/11] Promisify action creators in ActionCreatorsOf type --- packages/data/src/types.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index defb7218457c7c..68e05d7d589b91 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -6,7 +6,7 @@ import type { combineReducers as reduxCombineReducers } from 'redux'; type MapOf< T > = { [ name: string ]: T }; -export type ActionCreator = Function | Generator; +export type ActionCreator = ( ...args: any[] ) => any | Generator; export type Resolver = Function | Generator; export type Selector = Function; @@ -170,9 +170,17 @@ export type ConfigOf< S > = S extends StoreDescriptor< infer C > ? C : never; export type ActionCreatorsOf< Config extends AnyConfig > = Config extends ReduxStoreConfig< any, infer ActionCreators, any > - ? ActionCreators + ? PromisifiedActionCreator< ActionCreators > : never; +// When dispatching an action creator, the return value is a promise. +type PromisifiedActionCreator< ActionCreators extends MapOf< ActionCreator > > = + { + [ Action in keyof ActionCreators ]: ( + ...args: Parameters< ActionCreators[ Action ] > + ) => Promise< void >; + }; + type SelectorsOf< Config extends AnyConfig > = Config extends ReduxStoreConfig< any, any, From 943ff9c839fb2ee0f93e6c6cf8d4c6d1de47efd7 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Wed, 12 Jul 2023 16:33:40 -0700 Subject: [PATCH 02/11] Add singular and plural helper types --- packages/data/src/types.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index 68e05d7d589b91..6a1142ce5b17be 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -170,16 +170,22 @@ export type ConfigOf< S > = S extends StoreDescriptor< infer C > ? C : never; export type ActionCreatorsOf< Config extends AnyConfig > = Config extends ReduxStoreConfig< any, infer ActionCreators, any > - ? PromisifiedActionCreator< ActionCreators > + ? PromisifiedActionCreators< ActionCreators > : never; -// When dispatching an action creator, the return value is a promise. -type PromisifiedActionCreator< ActionCreators extends MapOf< ActionCreator > > = - { - [ Action in keyof ActionCreators ]: ( - ...args: Parameters< ActionCreators[ Action ] > - ) => Promise< void >; - }; +export type PromisifiedActionCreators< + ActionCreators extends MapOf< ActionCreator > +> = { + [ Action in keyof ActionCreators ]: PromisifyActionCreator< + ActionCreators[ Action ] + >; +}; + +// A dispatched action returns a Promise. This helper extends the original action +// creator, so that consumers know that they are dealing with a Promise. +export type PromisifyActionCreator< Action extends ActionCreator > = ( + ...args: Parameters< Action > +) => Promise< void >; type SelectorsOf< Config extends AnyConfig > = Config extends ReduxStoreConfig< any, From 3046bf7b8d255cda06479ec9f90c2991dc39da05 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Wed, 12 Jul 2023 16:46:13 -0700 Subject: [PATCH 03/11] Add returntype for promisified action creators --- packages/data/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index 6a1142ce5b17be..5cf4660dc9b354 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -185,7 +185,7 @@ export type PromisifiedActionCreators< // creator, so that consumers know that they are dealing with a Promise. export type PromisifyActionCreator< Action extends ActionCreator > = ( ...args: Parameters< Action > -) => Promise< void >; +) => Promise< ReturnType< Action > >; type SelectorsOf< Config extends AnyConfig > = Config extends ReduxStoreConfig< any, From a9aad26a5f6a29b50a4c01d8a79dafb18acbff59 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Wed, 12 Jul 2023 16:49:35 -0700 Subject: [PATCH 04/11] Add changelog entry --- packages/data/CHANGELOG.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index b3bb0e651c7045..1b2e2a052a6e22 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Bug Fix + +- The return type for a dispatched action has been changed to a Promise, which matches how actions work. + ## 9.8.0 (2023-07-20) ## 9.7.0 (2023-07-05) @@ -62,7 +66,7 @@ ### Breaking Changes -– Add TypeScript types to the built package (via "types": "build-types" in the package.json) +– Add TypeScript types to the built package (via "types": "build-types" in the package.json) ### Bug Fix @@ -100,9 +104,9 @@ ### New Features -- Enabled thunks by default for all stores and removed the `__experimentalUseThunks` flag. -- Store the resolution errors in store metadata and expose them using `hasResolutionFailed` the `getResolutionError` meta-selectors ([#38669](https://github.com/WordPress/gutenberg/pull/38669)). -- Expose the resolution status (undefined, resolving, finished, error) via the `getResolutionState` meta-selector ([#38669](https://github.com/WordPress/gutenberg/pull/38669)). +- Enabled thunks by default for all stores and removed the `__experimentalUseThunks` flag. +- Store the resolution errors in store metadata and expose them using `hasResolutionFailed` the `getResolutionError` meta-selectors ([#38669](https://github.com/WordPress/gutenberg/pull/38669)). +- Expose the resolution status (undefined, resolving, finished, error) via the `getResolutionState` meta-selector ([#38669](https://github.com/WordPress/gutenberg/pull/38669)). ## 6.2.1 (2022-02-10) From 9566b700fb675080c3a499c38694e4c442e8754b Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 13 Jul 2023 14:20:07 -0700 Subject: [PATCH 05/11] Update plain dispatch to handle string store descriptor --- packages/data/src/dispatch.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/data/src/dispatch.ts b/packages/data/src/dispatch.ts index e75eb8eb7bffcb..c1f28c21187cce 100644 --- a/packages/data/src/dispatch.ts +++ b/packages/data/src/dispatch.ts @@ -1,12 +1,7 @@ /** * Internal dependencies */ -import type { - ActionCreatorsOf, - AnyConfig, - ConfigOf, - StoreDescriptor, -} from './types'; +import type { AnyConfig, StoreDescriptor, DispatchReturn } from './types'; import defaultRegistry from './default-registry'; /** @@ -28,8 +23,10 @@ import defaultRegistry from './default-registry'; * ``` * @return Object containing the action creators. */ -export function dispatch< T extends StoreDescriptor< AnyConfig > >( - storeNameOrDescriptor: string | T -): ActionCreatorsOf< ConfigOf< T > > { +export function dispatch< + StoreNameOrDescriptor extends StoreDescriptor< AnyConfig > | string +>( + storeNameOrDescriptor: StoreNameOrDescriptor +): DispatchReturn< StoreNameOrDescriptor > { return defaultRegistry.dispatch( storeNameOrDescriptor ); } From 006048efbb98a497c5f62a985f3ab1477cfa1745 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 13 Jul 2023 14:21:04 -0700 Subject: [PATCH 06/11] Update PromisifiedActionCreator to handle thunks --- packages/data/src/types.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index 5cf4660dc9b354..2502bdc1d540c5 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -43,6 +43,7 @@ export interface ReduxStoreConfig< controls?: MapOf< Function >; } +// Return type for the useSelect() hook. export type UseSelectReturn< F extends MapSelect | StoreDescriptor< any > > = F extends MapSelect ? ReturnType< F > @@ -50,6 +51,7 @@ export type UseSelectReturn< F extends MapSelect | StoreDescriptor< any > > = ? CurriedSelectorsOf< F > : never; +// Return type for the useDispatch() hook. export type UseDispatchReturn< StoreNameOrDescriptor > = StoreNameOrDescriptor extends StoreDescriptor< any > ? ActionCreatorsOf< ConfigOf< StoreNameOrDescriptor > > @@ -59,9 +61,12 @@ export type UseDispatchReturn< StoreNameOrDescriptor > = export type DispatchFunction = < StoreNameOrDescriptor >( store: StoreNameOrDescriptor -) => StoreNameOrDescriptor extends StoreDescriptor< any > - ? ActionCreatorsOf< ConfigOf< StoreNameOrDescriptor > > - : any; +) => DispatchReturn< StoreNameOrDescriptor >; + +export type DispatchReturn< StoreNameOrDescriptor > = + StoreNameOrDescriptor extends StoreDescriptor< any > + ? ActionCreatorsOf< ConfigOf< StoreNameOrDescriptor > > + : unknown; export type MapSelect = ( select: SelectFunction, @@ -185,7 +190,12 @@ export type PromisifiedActionCreators< // creator, so that consumers know that they are dealing with a Promise. export type PromisifyActionCreator< Action extends ActionCreator > = ( ...args: Parameters< Action > -) => Promise< ReturnType< Action > >; +) => Promise< + ReturnType< Action > extends ( ..._args: any[] ) => any + ? // Thunks return another function, so we unwrap the return type twice in that scenario. + ReturnType< ReturnType< Action > > + : ReturnType< Action > +>; type SelectorsOf< Config extends AnyConfig > = Config extends ReduxStoreConfig< any, From e08fd01f2234ecd84133e80c72bda209976b6103 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 13 Jul 2023 14:21:41 -0700 Subject: [PATCH 07/11] Update generated docs --- packages/data/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/data/README.md b/packages/data/README.md index 444548dfe982ff..e9b3a2c192d521 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -499,11 +499,11 @@ dispatch( myCustomStore ).setPrice( 'hammer', 9.75 ); _Parameters_ -- _storeNameOrDescriptor_ `string | T`: The store descriptor. The legacy calling convention of passing the store name is also supported. +- _storeNameOrDescriptor_ `StoreNameOrDescriptor`: The store descriptor. The legacy calling convention of passing the store name is also supported. _Returns_ -- `ActionCreatorsOf< ConfigOf< T > >`: Object containing the action creators. +- `DispatchReturn< StoreNameOrDescriptor >`: Object containing the action creators. ### plugins From de0aeae50515b19d162688c5c8a0eac140f0f00f Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 13 Jul 2023 14:26:37 -0700 Subject: [PATCH 08/11] Clarify comments --- packages/data/src/types.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index 2502bdc1d540c5..12c0e726c3dabd 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -178,6 +178,9 @@ export type ActionCreatorsOf< Config extends AnyConfig > = ? PromisifiedActionCreators< ActionCreators > : never; +// Takes an object containing all action creators for a store and updates the +// return type of each action creator to account for internal registry details -- +// for example, dispatched actions are wrapped with a Promise. export type PromisifiedActionCreators< ActionCreators extends MapOf< ActionCreator > > = { @@ -186,14 +189,13 @@ export type PromisifiedActionCreators< >; }; -// A dispatched action returns a Promise. This helper extends the original action -// creator, so that consumers know that they are dealing with a Promise. +// Wraps action creator return types with a Promise -- also handles thunks by +// extracting the return type of the inner function of the thunk's action creator. export type PromisifyActionCreator< Action extends ActionCreator > = ( ...args: Parameters< Action > ) => Promise< ReturnType< Action > extends ( ..._args: any[] ) => any - ? // Thunks return another function, so we unwrap the return type twice in that scenario. - ReturnType< ReturnType< Action > > + ? ReturnType< ReturnType< Action > > // Thunks need to be unwrapped twice. : ReturnType< Action > >; From 1b9b7adba3fe735b72d78c18e052497939c43c65 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 13 Jul 2023 14:48:56 -0700 Subject: [PATCH 09/11] Account for async thunks --- packages/data/src/types.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index 12c0e726c3dabd..c4d8ac375f7b13 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -190,15 +190,24 @@ export type PromisifiedActionCreators< }; // Wraps action creator return types with a Promise -- also handles thunks by -// extracting the return type of the inner function of the thunk's action creator. +// extracting the return type of the inner function of the thunk's action creator, +// and by accounting for thunks that return a Promise. export type PromisifyActionCreator< Action extends ActionCreator > = ( ...args: Parameters< Action > ) => Promise< ReturnType< Action > extends ( ..._args: any[] ) => any - ? ReturnType< ReturnType< Action > > // Thunks need to be unwrapped twice. + ? ThunkReturnType< Action > : ReturnType< Action > >; +// A thunk is an action creator which returns a function, which can optionally +// return a Promise. The double ReturnType unwraps the innermost function's +// return type, and Awaited gets the type the Promise resolves to. If the return +// type is not a Promise, Awaited returns that original type. +export type ThunkReturnType< Action extends ActionCreator > = Awaited< + ReturnType< ReturnType< Action > > +>; + type SelectorsOf< Config extends AnyConfig > = Config extends ReduxStoreConfig< any, any, From cc4ca6a80f995b7f8136916748b114afbf031630 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 13 Jul 2023 14:49:03 -0700 Subject: [PATCH 10/11] Update changelog --- packages/data/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index 1b2e2a052a6e22..793d50910089a5 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -4,7 +4,8 @@ ### Bug Fix -- The return type for a dispatched action has been changed to a Promise, which matches how actions work. +- Update the type definitions for dispatched actions by accounting for Promisified return values and thunks. Previously, a dispatched action's return type was the same as the return type of the original action creator, which did not account for how dispatch works internally. (Plain actions get wrapped in a Promise, and thunk actions ultimately resolve to the innermost function's return type). +- Update the type definition for dispatch() to handle string store descriptors correctly. ## 9.8.0 (2023-07-20) From 8cbdc88b247ff56fddc2d5f0b8e0d6855760248d Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 13 Jul 2023 15:06:21 -0700 Subject: [PATCH 11/11] Make comment less redundant --- packages/data/src/types.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index c4d8ac375f7b13..af8cf823852755 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -189,9 +189,7 @@ export type PromisifiedActionCreators< >; }; -// Wraps action creator return types with a Promise -- also handles thunks by -// extracting the return type of the inner function of the thunk's action creator, -// and by accounting for thunks that return a Promise. +// Wraps action creator return types with a Promise and handles thunks. export type PromisifyActionCreator< Action extends ActionCreator > = ( ...args: Parameters< Action > ) => Promise<