diff --git a/src/sidebar/store/create-store.js b/src/sidebar/store/create-store.js index 0377832ad8a..c4b3d69d6fa 100644 --- a/src/sidebar/store/create-store.js +++ b/src/sidebar/store/create-store.js @@ -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} [Temp=MapContravariant] + * @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[]} modules + * @template {readonly Module[]} 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 + * @return {StoreFromModule>} */ export function createStore(modules, initArgs = [], middleware = []) { /** @type {Record} */ @@ -241,7 +260,7 @@ export function createStore(modules, initArgs = [], middleware = []) { } Object.assign(store, selectorMethods); - return store; + return /** @type {any} */ (store); } /** diff --git a/src/sidebar/store/index.js b/src/sidebar/store/index.js index 949b82147ac..af24e70894d 100644 --- a/src/sidebar/store/index.js +++ b/src/sidebar/store/index.js @@ -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} StoreFromModule - */ - -/** - * @typedef {StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule & - * StoreFromModule - * } SidebarStore - */ +/** @typedef {ReturnType} 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); }