Commit 31ac4c8a authored by Robert Knight's avatar Robert Knight

Determine type of sidebar store automatically

Avoid the need to manually create the `SidebarStore` type by inferring
it automatically. This works as follows:

 1. The `modules` argument to `createStore` has been converted to a
    tuple type (`[ModuleA, ModuleB, ...]`)
 2. The type of a module that would result from merging all the
    individual modules (`ModuleA & ModuleB ...`) is computed
    using a `TupleToIntersection` utility type
 3. `StoreFromModule` is used to compute the type of the store that the
    merged module would produce
parent 1326d1a9
......@@ -149,6 +149,24 @@ function assignOnce(target, source) {
return Object.assign(target, source);
}
/**
* @template T
* @typedef {{[K in keyof T]: (x: T[K]) => void }} MapContravariant
*/
/**
* Utility that turns a tuple type `[A, B, C]` into an intersection `A & B & C`.
*
* The implementation is magic adapted from
* https://github.com/microsoft/TypeScript/issues/28323. Roughly speaking it
* works by computing a type that could be assigned to any position in the
* tuple, which must be the intersection of all the tuple element types.
*
* @template T
* @template {Record<number, unknown>} [Temp=MapContravariant<T>]
* @typedef {Temp[number] extends (x: infer U) => unknown ? U : never} TupleToIntersection
*/
/**
* Create a Redux store from a set of _modules_.
*
......@@ -173,10 +191,11 @@ function assignOnce(target, source) {
* `use-store.js`. This returns a proxy which enables UI components to observe
* what store state a component depends upon and re-render when it changes.
*
* @param {Module<any,any,any,any>[]} modules
* @template {readonly Module<any,any,any,any>[]} Modules
* @param {Modules} modules
* @param {any[]} [initArgs] - Arguments to pass to each state module's `initialState` function
* @param {any[]} [middleware] - List of additional Redux middlewares to use
* @return Store<any,any,any>
* @return {StoreFromModule<TupleToIntersection<Modules>>}
*/
export function createStore(modules, initArgs = [], middleware = []) {
/** @type {Record<string, unknown>} */
......@@ -241,7 +260,7 @@ export function createStore(modules, initArgs = [], middleware = []) {
}
Object.assign(store, selectorMethods);
return store;
return /** @type {any} */ (store);
}
/**
......
......@@ -17,30 +17,7 @@ import { sidebarPanelsModule } from './modules/sidebar-panels';
import { toastMessagesModule } from './modules/toast-messages';
import { viewerModule } from './modules/viewer';
/**
* @template M
* @typedef {import('./create-store').StoreFromModule<M>} StoreFromModule
*/
/**
* @typedef {StoreFromModule<activityModule> &
* StoreFromModule<annotationsModule> &
* StoreFromModule<defaultsModule> &
* StoreFromModule<directLinkedModule> &
* StoreFromModule<draftsModule> &
* StoreFromModule<filtersModule> &
* StoreFromModule<framesModule> &
* StoreFromModule<groupsModule> &
* StoreFromModule<linksModule> &
* StoreFromModule<realTimeUpdatesModule> &
* StoreFromModule<routeModule> &
* StoreFromModule<selectionModule> &
* StoreFromModule<sessionModule> &
* StoreFromModule<sidebarPanelsModule> &
* StoreFromModule<toastMessagesModule> &
* StoreFromModule<viewerModule>
* } SidebarStore
*/
/** @typedef {ReturnType<createSidebarStore>} SidebarStore */
/**
* Create the central state store for the sidebar application.
......@@ -52,13 +29,14 @@ import { viewerModule } from './modules/viewer';
* [1] https://redux.js.org
*
* @param {import('../../types/config').SidebarSettings} settings
* @return {SidebarStore}
* @inject
*/
export function createSidebarStore(settings) {
const middleware = [debugMiddleware];
const modules = [
// `const` type gives `modules` a tuple type, which allows `createStore`
// to infer properties (eg. action and selector methods) of returned store.
const modules = /** @type {const} */ ([
activityModule,
annotationsModule,
defaultsModule,
......@@ -75,8 +53,6 @@ export function createSidebarStore(settings) {
sidebarPanelsModule,
toastMessagesModule,
viewerModule,
];
return /** @type {SidebarStore} */ (
createStore(modules, [settings], middleware)
);
]);
return createStore(modules, [settings], middleware);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment