From 42c8b86b165b661444e50e65fc57a5b4bc9b9fdf Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 13:54:00 +0000 Subject: [PATCH] chore(js-ts): Replace AnyAction with RootAction for improved type safety Define a RootAction union type that combines all typed Redux action modules (UserAction, NavigationAction, OnboardingActionTypes, SecurityAction, SdkAction, FiatOrdersAction, iAccountActions, iEventAction) with Action as a fallback for untyped JS actions and RTK slice actions. Replace AnyAction usage in: - persistReducer (app/store/index.ts) - ReduxStore type (app/core/redux/types.ts) - useThunkDispatch hook (app/components/hooks/useThunkDispatch.ts) - DeeplinkManager (app/core/DeeplinkManager/DeeplinkManager.ts) This prevents arbitrary property access on action objects without proper type narrowing, improving type safety across the Redux store. As more action modules are migrated to TypeScript, they should be added to the RootAction union. Co-Authored-By: Phil Bedford --- app/components/hooks/useThunkDispatch.ts | 7 +++--- app/core/DeeplinkManager/DeeplinkManager.ts | 11 +++------ app/core/redux/types.ts | 7 +++--- app/reducers/index.ts | 27 ++++++++++++++++++++- app/store/index.ts | 6 ++--- 5 files changed, 38 insertions(+), 20 deletions(-) diff --git a/app/components/hooks/useThunkDispatch.ts b/app/components/hooks/useThunkDispatch.ts index 6c7013d4c91..eb13d2f9299 100644 --- a/app/components/hooks/useThunkDispatch.ts +++ b/app/components/hooks/useThunkDispatch.ts @@ -1,12 +1,11 @@ import { ThunkAction as ReduxThunkAction, ThunkDispatch } from 'redux-thunk'; import { useDispatch } from 'react-redux'; -import { AnyAction } from 'redux'; -import { RootState } from '../../reducers'; +import { RootAction, RootState } from '../../reducers'; -export type ThunkAction = ReduxThunkAction; +export type ThunkAction = ReduxThunkAction; function useThunkDispatch() { - return useDispatch>(); + return useDispatch>(); } export default useThunkDispatch; diff --git a/app/core/DeeplinkManager/DeeplinkManager.ts b/app/core/DeeplinkManager/DeeplinkManager.ts index 114d1a60a66..5773f86e2f0 100644 --- a/app/core/DeeplinkManager/DeeplinkManager.ts +++ b/app/core/DeeplinkManager/DeeplinkManager.ts @@ -2,7 +2,8 @@ import { NavigationProp, ParamListBase } from '@react-navigation/native'; import { ParseOutput } from 'eth-url-parser'; -import { AnyAction, Dispatch, Store } from 'redux'; +import { Dispatch } from 'redux'; +import { RootAction } from '../../reducers'; import handleBrowserUrl from './Handlers/handleBrowserUrl'; import handleEthereumUrl from './Handlers/handleEthereumUrl'; import handleRampUrl from './Handlers/handleRampUrl'; @@ -14,18 +15,14 @@ import { RampType } from '../../reducers/fiatOrders/types'; class DeeplinkManager { public navigation: NavigationProp; public pendingDeeplink: string | null; - // TODO: Replace "any" with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public dispatch: Dispatch; + public dispatch: Dispatch; constructor({ navigation, dispatch, }: { navigation: NavigationProp; - // TODO: Replace "any" with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - dispatch: Store['dispatch']; + dispatch: Dispatch; }) { this.navigation = navigation; this.pendingDeeplink = null; diff --git a/app/core/redux/types.ts b/app/core/redux/types.ts index 09fff70ce7e..13ea519ceb5 100644 --- a/app/core/redux/types.ts +++ b/app/core/redux/types.ts @@ -1,8 +1,7 @@ -import { AnyAction, Store } from 'redux'; -import { RootState } from '../../reducers'; +import { Store } from 'redux'; +import { RootAction, RootState } from '../../reducers'; /** * Redux store type - * TODO: Replace AnyAction with union type of all actions */ -export type ReduxStore = Store; +export type ReduxStore = Store; diff --git a/app/reducers/index.ts b/app/reducers/index.ts index d5dc278c4cd..1d82957f35e 100644 --- a/app/reducers/index.ts +++ b/app/reducers/index.ts @@ -19,7 +19,15 @@ import collectiblesReducer from './collectibles'; import navigationReducer, { NavigationState } from './navigation'; import networkOnboardReducer from './networkSelector'; import securityReducer, { SecurityState } from './security'; -import { combineReducers, Reducer } from 'redux'; +import { Action, combineReducers, Reducer } from 'redux'; +import { UserAction } from '../actions/user/types'; +import { NavigationAction } from '../actions/navigation/types'; +import { OnboardingActionTypes } from '../actions/onboarding'; +import { Action as SecurityAction } from '../actions/security'; +import { Action as SdkAction } from '../actions/sdk'; +import { Action as FiatOrdersAction } from './fiatOrders/types'; +import { iAccountActions } from '../actions/accounts'; +import { iEventAction } from '../actions/rpcEvents'; import experimentalSettingsReducer from './experimentalSettings'; import { EngineState } from '../core/Engine'; import rpcEventReducer from './rpcEvents'; @@ -126,6 +134,23 @@ export interface RootState { performance?: PerformanceState; } +/** + * Union of all typed Redux actions in the app. + * As more action modules are migrated to TypeScript with proper types, + * add them to this union. {@link Action Action} serves as a base + * for untyped JS actions and RTK slice actions. + */ +export type RootAction = + | UserAction + | NavigationAction + | OnboardingActionTypes + | SecurityAction + | SdkAction + | FiatOrdersAction + | iAccountActions + | iEventAction + | Action; + const baseReducers = { legalNotices: legalNoticesReducer, collectibles: collectiblesReducer, diff --git a/app/store/index.ts b/app/store/index.ts index 5f82fa525f2..1558bdec9a6 100644 --- a/app/store/index.ts +++ b/app/store/index.ts @@ -1,9 +1,8 @@ -import { AnyAction } from 'redux'; import { configureStore } from '@reduxjs/toolkit'; import { persistStore, persistReducer, Persistor } from 'redux-persist'; import createSagaMiddleware from 'redux-saga'; import { rootSaga } from './sagas'; -import rootReducer, { RootState } from '../reducers'; +import rootReducer, { RootAction, RootState } from '../reducers'; import ReadOnlyNetworkStore from '../util/test/network-store'; import { isE2E } from '../util/test/utils'; import { trace, endTrace, TraceName, TraceOperation } from '../util/trace'; @@ -16,8 +15,7 @@ import ReduxService, { ReduxStore } from '../core/redux'; import { onPersistedDataLoaded } from '../actions/user'; import { toggleBasicFunctionality } from '../actions/settings'; -// TODO: Improve type safety by using real Action types instead of `AnyAction` -const pReducer = persistReducer( +const pReducer = persistReducer( persistConfig, rootReducer, );