redux-dynamic-modules icon indicating copy to clipboard operation
redux-dynamic-modules copied to clipboard

Update generic IModule interface

Open kazamov opened this issue 5 years ago • 9 comments

Please update IModule interface, so it will be possible to set Action type, e.g.

export interface IModule<State, Action = AnyAction> { /** * Id of the module / id: string; /* * Reducers for the module / reducerMap?: ReducersMapObject<State, Action >; /* * Middlewares to add to the store / middlewares?: Middleware[]; /* * These actions are dispatched immediately after adding the module in the store / initialActions?: Action[]; /* * These actions are dispatched immediatly before removing the module from the store / finalActions?: Action[]; /* * Specifies if the module is retained forever in the store */ retained?: boolean; }

kazamov avatar Oct 31 '19 14:10 kazamov

Question: what shape are your actions such that they aren't compatible with redux's AnyAction interface? The AnyAction interface only requires a type field and lets any other assigned field be any.

codeocelot avatar Nov 04 '19 23:11 codeocelot

I am using 'flux-standard-action' library typings: FSAAuto and ErrorFSAAuto. Link to typings file https://github.com/redux-utilities/flux-standard-action/blob/master/src/index.d.ts.

I have a reducer export function appReducer(state: AppState = initialAppState, action: AppActions): AppState {...}

Where AppActions is export type AppActions = FSAAuto<AppActionTypes.REQUEST_APP_METADATA, AppMetadataPayload> | FSAAuto<AppActionTypes.REQUEST_APP_METADATA_SUCCEED, App> | ErrorFSAAuto<AppActionTypes.REQUEST_APP_METADATA_FAILED>

In my module export const AppModule: ISagaModule<AppAwareState> = { id: 'appModule', reducerMap: { appModule: appReducer, }, // TODO: fix typing issue sagas: [rootSaga], };

I see a TS error:

(property) IModule<AppAwareState>.reducerMap?: ReducersMapObject<AppAwareState, AnyAction> | undefined Reducers for the module

Type '{ appModule: (state: AppState | undefined, action: AppActions) => AppState; }' is not assignable to type 'ReducersMapObject<AppAwareState, AnyAction>'. Types of property 'appModule' are incompatible. Type '(state: AppState | undefined, action: AppActions) => AppState' is not assignable to type 'Reducer<AppState, AnyAction>'. Types of parameters 'action' and 'action' are incompatible. Type 'AnyAction' is not assignable to type 'AppActions'. Type 'AnyAction' is not assignable to type 'ErrorFluxStandardActionWithPayload<AppActionTypes.REQUEST_APP_METADATA_FAILED, Error, undefined>'. Property 'error' is missing in type 'AnyAction' but required in type 'ErrorFluxStandardAction<AppActionTypes.REQUEST_APP_METADATA_FAILED, Error, undefined>'.ts(2322) index.d.ts(47, 3): 'error' is declared here. Contracts.d.ts(13, 5): The expected type comes from property 'reducerMap' which is declared here on type 'ISagaModule<AppAwareState>'

Thus it would be nice to have possibility to set own action types. By default it can be set to AnyAction

kazamov avatar Nov 05 '19 08:11 kazamov

@kazamov did you figure this out? If you have a repo where I can see the problem, that may better help me understand your problem.

codeocelot avatar Nov 21 '19 21:11 codeocelot

I am encountering the same problem. I am trying to use connected-react-router and getting the following error:

TypeScript error in /myapp/src/app/store/module.ts(13,3): Type '{ router: Reducer<RouterState<History.PoorMansUnknown>, LocationChangeAction<History.PoorMansUnknown>>; }' is not assignable to type 'ReducersMapObject<AppModuleState, AnyAction>'. Types of property 'router' are incompatible. Type 'Reducer<RouterState<PoorMansUnknown>, LocationChangeAction<PoorMansUnknown>>' is not assignable to type 'Reducer<RouterState<PoorMansUnknown>, AnyAction>'. Types of parameters 'action' and 'action' are incompatible. Type 'AnyAction' is not assignable to type 'LocationChangeAction<PoorMansUnknown>'. TS2322

Here is my code:

import {ISagaModule} from 'redux-dynamic-modules-saga'
import {connectRouter, routerMiddleware, RouterState} from 'connected-react-router'
import history from 'utils/history'

const routerReducer = connectRouter(history)

interface AppModuleState {
  router: RouterState,
}

const AppModule: ISagaModule<AppModuleState> = {
  id: 'app',
  reducerMap: {
    router: routerReducer,
  },
  middlewares: [
    routerMiddleware(history),
  ],
  sagas: [],
}

export default AppModule

I believe this might be because of strict contravariance and the way that RouterState is defined by connected-react-router. I was able to workaround it by adding the following assertion:

import {Reducer} from 'redux'
.
.
.
const routerReducer = connectRouter(history) as Reducer<RouterState>

I don't know if the best way to handle this is to change the signature of IModule to accept a generic action type, or to just assign manually as I've done above. Just thought this might be helpful to anyone else who encounters this issue.

bloomdido avatar Mar 03 '20 06:03 bloomdido

Hi @bloomdido.

I have found a workaround for this issue. If cast the type of "reducerMap" property to ReducersMapObject<YOUR_REDUCER_MAP_TYPE>, everything is fine. E.g.

export const RoleModule: ISagaModule<RoleAwareState> = {
  id: 'roleModule',
  reducerMap: {
	roleModule: roleReducer,
  } as ReducersMapObject<RoleAwareState>,
  sagas: [rootSaga],
};

kazamov avatar Mar 03 '20 10:03 kazamov

@kazamov Yeah, I think both of our solutions are about getting around Typescript's strict contravariance for callbacks.

From what I understand, the issue is that any action can always be an AnyAction, but an AnyAction cannot always be any action.

bloomdido avatar Mar 03 '20 19:03 bloomdido

From what I understand, the issue is that any action can always be an AnyAction, but an AnyAction cannot always be any action.

@bloomdido , This seems to be the same issue I am now encountering with putting a ThunkAction in the initialActions part of the module. Any suggestions on resolving this? I have not been able to find a way to eliminate this problem.

export const fetchToplineData = (): ThunkAction<any, any, any, AnyAction> => {
    return (dispatch, getState) => {
        const mockData = [{ a: 1, b: 2, c: 3, s: getState() }];
        dispatch(toplineDataAvailable(mockData));
    };
};
export function getToplineModule(): IModule<any> {
    return {
        // Unique id of the module
        id: "topline",
        // Maps the Store key to the reducer
        reducerMap: {
            topline: toplineReducer,
        },
        initialActions: [fetchToplineData()], // this line errors
        finalActions: [],
    };
}

Error Produced TS2741: Property 'type' is missing in type 'ThunkAction<any, any, any, AnyAction>' but required in type 'AnyAction'.

forgo avatar Aug 11 '20 18:08 forgo

From what I understand, the issue is that any action can always be an AnyAction, but an AnyAction cannot always be any action.

@bloomdido , This seems to be the same issue I am now encountering with putting a ThunkAction in the initialActions part of the module. Any suggestions on resolving this? I have not been able to find a way to eliminate this problem.

export const fetchToplineData = (): ThunkAction<any, any, any, AnyAction> => {
    return (dispatch, getState) => {
        const mockData = [{ a: 1, b: 2, c: 3, s: getState() }];
        dispatch(toplineDataAvailable(mockData));
    };
};
export function getToplineModule(): IModule<any> {
    return {
        // Unique id of the module
        id: "topline",
        // Maps the Store key to the reducer
        reducerMap: {
            topline: toplineReducer,
        },
        initialActions: [fetchToplineData()], // this line errors
        finalActions: [],
    };
}

Error Produced TS2741: Property 'type' is missing in type 'ThunkAction<any, any, any, AnyAction>' but required in type 'AnyAction'.

Hi @forgo, I described the workaround above, you should cast 'reducerMap' to proper type

kazamov avatar Aug 11 '20 18:08 kazamov

Hi @kazamov , just to be clear, my error is on initialActions not the reducerMap. Not sure if that is related. I had already tried your example above casting reducerMap as ReducersMapObject<any> and ReducersMapObject<any, any> and still have this error before posting here.

forgo avatar Aug 11 '20 19:08 forgo