diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index b3bb0e651c7045..793d50910089a5 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### Bug Fix + +- 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) ## 9.7.0 (2023-07-05) @@ -62,7 +67,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 +105,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) 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 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 ); } diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index defb7218457c7c..af8cf823852755 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; @@ -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, @@ -170,9 +175,37 @@ 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 + ? 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 > +> = { + [ Action in keyof ActionCreators ]: PromisifyActionCreator< + ActionCreators[ Action ] + >; +}; + +// Wraps action creator return types with a Promise and handles thunks. +export type PromisifyActionCreator< Action extends ActionCreator > = ( + ...args: Parameters< Action > +) => Promise< + ReturnType< Action > extends ( ..._args: any[] ) => any + ? 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,