@wordpress/data
Version:
Data module for WordPress.
346 lines • 13.3 kB
TypeScript
/**
* External dependencies
*/
import type { combineReducers as reduxCombineReducers, Store as ReduxStore } from 'redux';
/**
* Internal dependencies
*/
import type { DataEmitter } from './utils/emitter';
import type { MetadataSelectors, MetadataActions } from './redux-store/metadata/types';
type MapOf<T> = {
[name: string]: T;
};
export type ActionCreator = (...args: any[]) => any | Generator;
export type Resolver = Function | Generator;
export type Selector = Function;
export type AnyConfig = ReduxStoreConfig<any, any, any>;
export interface StoreInstance<Config extends AnyConfig> {
getSelectors: () => SelectorsOf<Config>;
getActions: () => ActionCreatorsOf<Config>;
subscribe: (listener: () => void) => () => void;
}
export interface StoreDescriptor<Config extends AnyConfig = AnyConfig> {
/**
* Store Name
*/
name: string;
/**
* Creates a store instance
*/
instantiate: (registry: DataRegistry) => StoreInstance<Config>;
}
export interface ReduxStoreConfig<State, ActionCreators, Selectors> {
initialState?: State;
reducer: (state: any, action: any) => any;
actions?: ActionCreators;
resolvers?: MapOf<Resolver>;
selectors?: Selectors;
controls?: MapOf<Function>;
}
export type UseSelectReturn<F extends MapSelect | StoreDescriptor<any>> = F extends MapSelect ? ReturnType<F> : F extends StoreDescriptor<any> ? CurriedSelectorsOf<F> : never;
export type UseDispatchReturn<StoreNameOrDescriptor> = StoreNameOrDescriptor extends StoreDescriptor<any> ? ActionCreatorsOf<StoreNameOrDescriptor> : StoreNameOrDescriptor extends undefined ? DispatchFunction : any;
export type DispatchFunction = <StoreNameOrDescriptor>(store: StoreNameOrDescriptor) => DispatchReturn<StoreNameOrDescriptor>;
export type DispatchReturn<StoreNameOrDescriptor> = StoreNameOrDescriptor extends StoreDescriptor<any> ? ActionCreatorsOf<StoreNameOrDescriptor> : unknown;
export type MapSelect = (select: SelectFunction, registry: DataRegistry) => any;
export type SelectFunction = <S>(store: S) => CurriedSelectorsOf<S>;
/**
* Callback for store's `subscribe()` method that
* runs when the store data has changed.
*/
export type ListenerFunction = () => void;
export type CurriedSelectorsOf<S> = S extends StoreDescriptor<ReduxStoreConfig<any, any, infer Selectors>> ? {
[key in keyof Selectors]: CurriedState<Selectors[key]>;
} & MetadataSelectors<S> : never;
/**
* Like CurriedState but wraps the return type in a Promise.
* Used for resolveSelect where selectors return promises.
*
* For generic selectors that define PromiseCurriedSignature, that signature
* is used directly to preserve generic type parameters (which would otherwise
* be lost when using `infer`).
*/
type CurriedStateWithPromise<F> = F extends SelectorWithCustomCurrySignature & {
PromiseCurriedSignature: infer S;
} ? S : F extends SelectorWithCustomCurrySignature & {
CurriedSignature: (...args: infer P) => infer R;
} ? (...args: P) => Promise<R> : F extends (state: any, ...args: infer P) => infer R ? (...args: P) => Promise<R> : F;
/**
* Like CurriedSelectorsOf but each selector returns a Promise.
* Used for resolveSelect.
*/
export type CurriedSelectorsResolveOf<S> = S extends StoreDescriptor<ReduxStoreConfig<any, any, infer Selectors>> ? {
[key in keyof Selectors]: CurriedStateWithPromise<Selectors[key]>;
} : never;
/**
* Removes the first argument from a function.
*
* 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.
* 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;
/**
* 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<
* <K extends string | number>(
* 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 {
* <K extends string | number>(
* state: any,
* kind: K,
* key: K extends string ? 'one value' : false
* ): K;
*
* CurriedSignature: <K extends string | number>(
* kind: K,
* key: K extends string ? 'one value' : false
* ): K;
* }
* type CorrectlyInferredSignature = CurriedState<MySelectorSignature>
* // <K extends string | number>(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 {
CurriedSignature: Function;
PromiseCurriedSignature?: Function;
}
/**
* A store name or store descriptor, used throughout the API.
*/
export type StoreNameOrDescriptor = string | StoreDescriptor;
/**
* An isolated orchestrator of store registrations.
*
* Returned by `createRegistry`. Provides methods to register stores,
* select data, dispatch actions, and subscribe to changes.
*/
export interface DataRegistry {
batch: (callback: () => void) => void;
stores: Record<string, InternalStoreInstance>;
namespaces: Record<string, InternalStoreInstance>;
subscribe: (listener: ListenerFunction, storeNameOrDescriptor?: StoreNameOrDescriptor) => () => void;
select: {
<S extends StoreDescriptor<any>>(store: S): CurriedSelectorsOf<S>;
(store: StoreNameOrDescriptor): Record<string, (...args: any[]) => any>;
};
resolveSelect: {
<S extends StoreDescriptor<any>>(store: S): CurriedSelectorsResolveOf<S>;
(store: StoreNameOrDescriptor): Record<string, (...args: any[]) => Promise<any>>;
};
suspendSelect: {
<S extends StoreDescriptor<any>>(store: S): CurriedSelectorsOf<S>;
(store: StoreNameOrDescriptor): Record<string, (...args: any[]) => any>;
};
dispatch: {
<S extends StoreDescriptor<any>>(store: S): ActionCreatorsOf<S>;
(store: StoreNameOrDescriptor): Record<string, (...args: any[]) => any>;
};
use: (plugin: DataPlugin, options?: Record<string, unknown>) => DataRegistry;
register: (store: StoreDescriptor<any>) => void;
registerGenericStore: (name: string, store: StoreInstance<AnyConfig>) => void;
registerStore: (storeName: string, options: ReduxStoreConfig<any, any, any>) => ReduxStore;
__unstableMarkListeningStores: <T>(callback: () => T, ref: {
current: string[] | null;
}) => T;
}
/**
* The plugin function signature.
*/
export type DataPlugin = (registry: DataRegistry, options?: Record<string, unknown>) => Partial<DataRegistry>;
/**
* Status of a selector resolution.
*/
export type ResolutionStatus = 'resolving' | 'finished' | 'error';
/**
* State value for a single resolution.
*/
export type ResolutionState = {
status: 'resolving';
} | {
status: 'finished';
} | {
status: 'error';
error: Error | unknown;
};
/**
* A normalized resolver with a `fulfill` method and optional `isFulfilled`.
*/
export interface NormalizedResolver {
/**
* The function to call to fulfill the resolver.
*/
fulfill: (...args: any[]) => any;
/**
* Optional function to check if the resolver is already fulfilled.
*/
isFulfilled?: (state: any, ...args: any[]) => boolean;
/**
* Optional function to check if the resolver should be invalidated.
*/
shouldInvalidate?: (action: any, ...args: any[]) => boolean;
}
/**
* A bound selector with optional resolver metadata.
*/
export interface BoundSelector {
(...args: any[]): any;
/**
* Whether this selector has a resolver attached.
*/
hasResolver: boolean;
/**
* Optional function to normalize the arguments.
*/
__unstableNormalizeArgs?: (args: any[]) => any[];
/**
* Whether this selector is a registry selector.
*/
isRegistrySelector?: boolean;
/**
* The registry instance this selector is bound to.
*/
registry?: DataRegistry;
}
/**
* The shape of a store instance as seen internally by the registry.
* Extends the public StoreInstance with additional internal properties.
*/
export interface InternalStoreInstance<Config extends AnyConfig = AnyConfig> extends StoreInstance<Config> {
/**
* The Redux store instance (only for Redux-based stores).
*/
store?: ReduxStore;
/**
* The internal emitter for pause/resume batching.
*/
emitter: DataEmitter;
/**
* The combined reducer.
*/
reducer?: (state: any, action: any) => any;
/**
* Bound actions object.
*/
actions?: Record<string, ActionCreator>;
/**
* Bound selectors object.
*/
selectors?: Record<string, Selector>;
/**
* Resolver definitions.
*/
resolvers?: Record<string, NormalizedResolver>;
/**
* Returns resolve-wrapped selectors.
*/
getResolveSelectors?: () => Record<string, (...args: any[]) => Promise<any>>;
/**
* Returns suspense-wrapped selectors.
*/
getSuspendSelectors?: () => Record<string, (...args: any[]) => any>;
}
/**
* Control descriptor for the controls system.
*/
export interface ControlDescriptor {
/**
* The type of the control action.
*/
type: string;
/**
* The store key to target.
*/
storeKey: string;
/**
* The name of the selector (for select/resolveSelect controls).
*/
selectorName?: string;
/**
* The name of the action (for dispatch controls).
*/
actionName?: string;
/**
* Arguments for the selector or action.
*/
args: any[];
}
/**
* Storage interface (Web Storage API subset).
*/
export interface StorageInterface {
getItem: (key: string) => string | null;
setItem: (key: string, value: string) => void;
removeItem?: (key: string) => void;
clear?: VoidFunction;
}
export type ConfigOf<S> = S extends StoreDescriptor<infer C> ? C : never;
export type ActionCreatorsOf<T> = T extends StoreDescriptor<ReduxStoreConfig<any, infer ActionCreators, any>> ? PromisifiedActionCreators<ActionCreators> & MetadataActions<T> : T extends ReduxStoreConfig<any, infer ActionCreators, any> ? PromisifiedActionCreators<ActionCreators> : never;
export type PromisifiedActionCreators<ActionCreators> = {
[Action in keyof ActionCreators]: ActionCreators[Action] extends ActionCreator ? PromisifyActionCreator<ActionCreators[Action]> : ActionCreators[Action];
};
export type PromisifyActionCreator<Action extends ActionCreator> = (...args: Parameters<Action>) => Promise<ReturnType<Action> extends (..._args: any[]) => any ? ThunkReturnType<Action> : ReturnType<Action> extends Generator<any, infer TReturn, any> ? TReturn : ReturnType<Action>>;
export type ThunkReturnType<Action extends ActionCreator> = Awaited<ReturnType<ReturnType<Action>>>;
type SelectorsOf<Config extends AnyConfig> = Config extends ReduxStoreConfig<any, any, infer Selectors> ? {
[name in keyof Selectors]: Function;
} : never;
/**
* The argument object passed to every thunk function. When parameterized with
* a store descriptor, `dispatch`, `select`, and `resolveSelect` are fully
* typed against that store's actions and selectors.
*
* @example
* ```ts
* const myAction =
* ( id: number ) =>
* async ( { dispatch, select }: ThunkArgs< typeof myStore > ) => {
* const record = select.getRecord( id );
* dispatch.setLoading( true );
* };
* ```
*/
export interface ThunkArgs<S extends StoreDescriptor = StoreDescriptor, PrivateSelectors extends Record<string, Function> = {}, PrivateActions extends Record<string, ActionCreator> = {}> {
dispatch: ActionCreatorsOf<S> & PromisifiedActionCreators<PrivateActions> & {
<R>(thunk: (...args: any[]) => R): R;
(action: Record<string, unknown>): unknown;
};
select: CurriedSelectorsOf<S> & {
[key in keyof PrivateSelectors]: CurriedState<PrivateSelectors[key]>;
};
resolveSelect: CurriedSelectorsResolveOf<S>;
registry: DataRegistry;
}
export type combineReducers = typeof reduxCombineReducers;
export {};
//# sourceMappingURL=types.d.ts.map