UNPKG

@wordpress/data

Version:
346 lines 13.3 kB
/** * 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