diff --git a/client/__tests__/reducers/genesetsUI.test.ts b/client/__tests__/reducers/genesetsUI.test.ts index 0c0ca6a57..2bb4ce775 100644 --- a/client/__tests__/reducers/genesetsUI.test.ts +++ b/client/__tests__/reducers/genesetsUI.test.ts @@ -1,8 +1,8 @@ -import genesetsUIReducer from "../../src/reducers/genesetsUI"; +import genesetsUIReducer, { GeneSetsUIState } from "../../src/reducers/genesetsUI"; // Format: GeneSetsUI(state,action) -const initialState = { +const initialState: GeneSetsUIState = { createGenesetModeActive: false, isEditingGenesetName: false, isAddingGenesToGeneset: false, @@ -30,7 +30,7 @@ describe("geneset UI states", () => { }); test("geneset: disable create geneset mode", () => { expect( - genesetsUIReducer(undefined, { isEditingGenesetName: false }) + genesetsUIReducer(undefined, { type: "geneset: disable rename geneset mode", isEditingGenesetName: false }) ).toMatchObject(initialState); }); diff --git a/client/src/reducers/cascade.ts b/client/src/reducers/cascade.ts index 046c65203..1f355d41f 100644 --- a/client/src/reducers/cascade.ts +++ b/client/src/reducers/cascade.ts @@ -1,46 +1,59 @@ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. -export default function cascadeReducers(arg: any) { - /* - Combine a set of cascading reducers into a single reducer. Cascading - reducers are reducers which may rely on state computed by another reducer. - Therefore, they: - - must be composed in a particular order (currently, this is a simple - linear list of reducers, run in list order) - - must have access to partially updated "next state" so they can further - derive state. +import { AnyAction, Reducer } from "redux"; +import type { RootState } from "."; - Parameter is one of: - - a Map object - - an array of tuples, [ [key1, reducer1], [key2, reducer2], ... ] - Ie, cascadeReducers([ ["a", reduceA], ["b", reduceB] ]) +export type ReducerFunction = ( + prevStateForKey: RootState[keyof RootState], + action: AnyAction, + nextState?: RootState, + prevState?: RootState +) => RootState; - Each reducer will be called with the signature: - (prevState, action, sharedNextState, sharedPrevState) => newState +type CascadedReducers = + | [string, ReducerFunction][] + | Map; - cascadeReducers will build a composite newState object, much - like combinedReducers. Additional semantics: - - reducers guaranteed to be called in order - - each reducer will receive shared objects - */ +export default function cascadeReducers(arg: CascadedReducers): Reducer { + /** + * Combine a set of cascading reducers into a single reducer. Cascading + * reducers are reducers which may rely on state computed by another reducer. + * Therefore, they: + * - must be composed in a particular order (currently, this is a simple + * linear list of reducers, run in list order) + * - must have access to partially updated "next state" so they can further + * derive state. + * + * Parameter is one of: + * - a Map object + * - an array of tuples, [ [key1, reducer1], [key2, reducer2], ... ] + * Ie, cascadeReducers([ ["a", reduceA], ["b", reduceB] ]) + * + * Each reducer will be called with the signature: + * (prevState, action, sharedNextState, sharedPrevState) => newState + * cascadeReducers will build a composite newState object, much + * like combinedReducers. Additional semantics: + * - reducers guaranteed to be called in order + * - each reducer will receive shared objects + */ const reducers = arg instanceof Map ? arg : new Map(arg); const reducerKeys = [...reducers.keys()]; - // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - return (prevState: any, action: any) => { - const nextState = {}; + + return (prevState: RootState, action: AnyAction) => { + const nextState: RootState = {}; let stateChange = false; for (let i = 0, l = reducerKeys.length; i < l; i += 1) { const key = reducerKeys[i]; const reducer = reducers.get(key); - const prevStateForKey = prevState ? prevState[key] : undefined; - const nextStateForKey = reducer( - prevStateForKey, - action, - nextState, - prevState - ); - // @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message - nextState[key] = nextStateForKey; - stateChange = stateChange || nextStateForKey !== prevStateForKey; + if (reducer) { + const prevStateForKey = prevState ? prevState[key] : undefined; + const nextStateForKey = reducer( + prevStateForKey, + action, + nextState, + prevState + ); + nextState[key] = nextStateForKey; + stateChange = stateChange || nextStateForKey !== prevStateForKey; + } } return stateChange ? nextState : prevState; }; diff --git a/client/src/reducers/categoricalSelection.ts b/client/src/reducers/categoricalSelection.ts index ab710334d..015446bdb 100644 --- a/client/src/reducers/categoricalSelection.ts +++ b/client/src/reducers/categoricalSelection.ts @@ -20,7 +20,7 @@ const CategoricalSelection = ( state: CategoricalSelectionState, action: AnyAction, nextSharedState: RootState -) => { +): CategoricalSelectionState => { switch (action.type) { case "initial data load complete": case "subset to selection": diff --git a/client/src/reducers/centroidLabels.ts b/client/src/reducers/centroidLabels.ts index 5cd4767fa..5be992b60 100644 --- a/client/src/reducers/centroidLabels.ts +++ b/client/src/reducers/centroidLabels.ts @@ -1,4 +1,5 @@ -import type { Action } from "redux"; +import type { Action, AnyAction } from "redux"; +import type { RootState } from "."; export interface CentroidLabelsState { showLabels: boolean; @@ -14,15 +15,15 @@ const initialState: CentroidLabelsState = { const centroidLabels = ( state = initialState, - action: CentroidLabelsAction, - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - sharedNextState: any + action: AnyAction, + sharedNextState: RootState ): CentroidLabelsState => { const { colors: { colorAccessor }, } = sharedNextState; - const showLabels = action.showLabels ?? state.showLabels; + const showLabels = + (action as CentroidLabelsAction).showLabels ?? state.showLabels; switch (action.type) { case "color by categorical metadata": diff --git a/client/src/reducers/continuousSelection.ts b/client/src/reducers/continuousSelection.ts index 8923c2b68..0a3d6b151 100644 --- a/client/src/reducers/continuousSelection.ts +++ b/client/src/reducers/continuousSelection.ts @@ -1,4 +1,4 @@ -import type { Action } from "redux"; +import type { Action, AnyAction } from "redux"; import { makeContinuousDimensionName } from "../util/nameCreators"; @@ -16,7 +16,7 @@ export interface ContinuousSelectionState { const ContinuousSelection = ( state: ContinuousSelectionState = {}, - action: ContinuousSelectionAction + action: AnyAction ): ContinuousSelectionState => { switch (action.type) { case "reset subset": @@ -27,20 +27,20 @@ const ContinuousSelection = ( case "continuous metadata histogram start": case "continuous metadata histogram brush": case "continuous metadata histogram end": { - const name = makeContinuousDimensionName( - action.continuousNamespace, - action.selection - ); + const { continuousNamespace, selection, range } = + action as ContinuousSelectionAction; + + const name = makeContinuousDimensionName(continuousNamespace, selection); return { ...state, - [name]: action.range, + [name]: range, }; } case "continuous metadata histogram cancel": { - const name = makeContinuousDimensionName( - action.continuousNamespace, - action.selection - ); + const { continuousNamespace, selection } = + action as ContinuousSelectionAction; + + const name = makeContinuousDimensionName(continuousNamespace, selection); const { [name]: deletedField, ...newState } = state; return newState; } diff --git a/client/src/reducers/datasetMetadata.ts b/client/src/reducers/datasetMetadata.ts index a9e0abbc9..22c8b104d 100644 --- a/client/src/reducers/datasetMetadata.ts +++ b/client/src/reducers/datasetMetadata.ts @@ -1,9 +1,9 @@ /* - Dataset metadata reducer, modifies Portal collections-related state. + Dataset metadata reducer, modifies Portal collections-related state. */ // Core dependencies -import { Action } from "redux"; +import { Action, AnyAction } from "redux"; // App dependencies import { DatasetMetadata as IDatasetMetadata } from "../common/types/entities"; @@ -11,14 +11,14 @@ import { DatasetMetadata as IDatasetMetadata } from "../common/types/entities"; /* Action dispatched on successful response from dataset-metadata endpoint. */ -export interface DatasetMetdataAction extends Action { +export interface DatasetMetadataAction extends Action { datasetMetadata: IDatasetMetadata; error: string; portalUrl: string; } /* - Dataset metdata state; selected dataset ID and corresponding collection information. + Dataset metadata state; selected dataset ID and corresponding collection information. */ export interface DatasetMetadataState { datasetMetadata: IDatasetMetadata | null; @@ -36,7 +36,7 @@ const DatasetMetadata = ( datasetMetadata: null, portalUrl: null, }, - action: DatasetMetdataAction + action: AnyAction ): DatasetMetadataState => { switch (action.type) { case "initial data load start": @@ -45,19 +45,25 @@ const DatasetMetadata = ( loading: true, error: null, }; - case "dataset metadata load complete": + case "dataset metadata load complete": { + const { datasetMetadata, portalUrl } = action as DatasetMetadataAction; + return { ...state, loading: false, error: null, - datasetMetadata: action.datasetMetadata, - portalUrl: action.portalUrl, + datasetMetadata, + portalUrl, }; - case "initial data load error": + } + case "initial data load error": { + const { error } = action as DatasetMetadataAction; + return { ...state, - error: action.error, + error, }; + } default: return state; } diff --git a/client/src/reducers/genesetsUI.ts b/client/src/reducers/genesetsUI.ts index 6a11a7c10..3abfeaa11 100644 --- a/client/src/reducers/genesetsUI.ts +++ b/client/src/reducers/genesetsUI.ts @@ -1,15 +1,20 @@ +import { AnyAction } from "redux"; + +export interface GeneSetsUIState { + createGenesetModeActive: boolean; + isEditingGenesetName: string | false; + isAddingGenesToGeneset: string | false; +} /* Reducers for geneset UI-state. */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS. const GeneSetsUI = ( - state = { + state: GeneSetsUIState = { createGenesetModeActive: false, isEditingGenesetName: false, isAddingGenesToGeneset: false, }, - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. - action: any + action: AnyAction ) => { switch (action.type) { /** diff --git a/client/src/reducers/layoutChoice.ts b/client/src/reducers/layoutChoice.ts index faa62e161..f30404b7a 100644 --- a/client/src/reducers/layoutChoice.ts +++ b/client/src/reducers/layoutChoice.ts @@ -8,7 +8,8 @@ about commonly used names. Preferentially, pick in the following order: 4. give up, use the first available */ -import type { Action } from "redux"; +import type { Action, AnyAction } from "redux"; +import type { RootState } from "."; import { EmbeddingSchema, Schema } from "../common/types/schema"; function bestDefaultLayout(layouts: Array): string { @@ -39,9 +40,8 @@ export interface LayoutChoiceAction extends Action { const LayoutChoice = ( state: LayoutChoiceState, - action: LayoutChoiceAction, - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. - nextSharedState: any + action: AnyAction, + nextSharedState: RootState ): LayoutChoiceState => { switch (action.type) { case "initial data load complete": { @@ -55,7 +55,7 @@ const LayoutChoice = ( case "set layout choice": { const { schema } = nextSharedState.annoMatrix; - const current = action.layoutChoice; + const current = (action as LayoutChoiceAction).layoutChoice; const currentDimNames = schema.layout.obsByName[current].dims; return { ...state, current, currentDimNames }; } diff --git a/client/src/reducers/quickGenes.ts b/client/src/reducers/quickGenes.ts index 612f3b93d..15b7743c3 100644 --- a/client/src/reducers/quickGenes.ts +++ b/client/src/reducers/quickGenes.ts @@ -1,23 +1,28 @@ import uniq from "lodash.uniq"; import filter from "lodash.filter"; +import { Action, AnyAction } from "redux"; import type { RootState } from "."; import { track } from "../analytics"; import { EVENTS } from "../analytics/events"; -interface QuickGenesActions { - type: string; +interface State { + userDefinedGenes: string[]; + userDefinedGenesLoading: boolean; +} + +interface QuickGenesActions extends Action { gene: string; selection: string; data: string; } const quickGenes = ( - state: { userDefinedGenes: string[]; userDefinedGenesLoading: boolean } = { + state: State = { userDefinedGenes: [], userDefinedGenesLoading: false, }, - action: QuickGenesActions, + action: AnyAction, nextSharedState: RootState -) => { +): State => { switch (action.type) { case "request user defined gene started": { return { @@ -33,7 +38,10 @@ const quickGenes = ( } case "request user defined gene success": { const { userDefinedGenes } = state; - const _userDefinedGenes = uniq([...userDefinedGenes, action.gene]); + const { gene } = action as QuickGenesActions; + + const _userDefinedGenes = uniq([...userDefinedGenes, gene]); + return { ...state, userDefinedGenes: _userDefinedGenes, @@ -42,10 +50,10 @@ const quickGenes = ( } case "clear user defined gene": { const { userDefinedGenes } = state; - const newUserDefinedGenes = filter( - userDefinedGenes, - (d) => d !== action.gene - ); + const { gene } = action as QuickGenesActions; + + const newUserDefinedGenes = filter(userDefinedGenes, (d) => d !== gene); + return { ...state, userDefinedGenes: newUserDefinedGenes, @@ -56,7 +64,7 @@ const quickGenes = ( case "color by expression": case "set scatterplot x": case "set scatterplot y": { - const { selection, gene } = action; + const { selection, gene } = action as QuickGenesActions; const { controls } = nextSharedState; const { scatterplotXXaccessor, scatterplotYYaccessor } = controls;