Reputation: 424
I'm having a hard time refactor my reducers to TS since I'm using Immutable.js in my project This is my reducer:
export interface DashboardState {
componentId: number | null;
dashboard: number | null;
}
let defaultState: DashboardState = {
componentId: null,
dashboard: null,
};
export default function (state = Map<string, number>(defaultState), action):
Map<string, number> {
switch (action.type) {
case dashboardsActionsTypes.SET_PROJECT_VIEW:
return state.set('componentId', action.payload);
case dashboardsActionsTypes.SET_DASHBOARD_TYPE:
return state.set('dashboard', action.payload);
default:
return state;
}
}
I'm getting an error on the Map(defaultState) that says:
What I'm missing here?
Thanks
Upvotes: 1
Views: 540
Reputation: 42160
There are multiple ways to construct a Map
.
export function Map<K, V>(collection: Iterable<[K, V]>): Map<K, V>;
export function Map<V>(obj: {[key: string]: V}): Map<string, V>;
export function Map<K, V>(): Map<K, V>;
export function Map(): Map<any, any>;
The error on the first overload is expected because you definitely shouldn't match that one. Your object is closer to the second overload, but you don't match that overload automatically because it involves a string
index signature which DashboardState
does not have.
The quick and dirty solution here involves setting a generic on the Map
function. As you can see, the second overload is the only one with just <V>
instead of <K, V>
. So you can match this overload by setting just a value type for the Map
. In this case the value type of DashboardState
is any
.
state = Map<any>(defaultState)
The type support with this setup is extremely weak. The keys of state
can be any string
and the values can be absolutely anything. Why bother using Typescript at all if you can't get any useful information out of it?
I recommend using the official Redux Toolkit which has strong typescript support as well as support for immutability using Immer. And replace the any
in your DashboardState
with the actual types.
Edit:
After viewing your edit, I recommend that you use optional properties (number | undefined
) instead of number | null
since Map.get()
includes undefined
along with the expected value. So you would get number | null | undefined
. If using optional properties then you don't actually need an initial state at all. You can use an empty Map
.
We can also limit the keys to just the keys of DashboardState
.
Use these utilities on any state type:
export type MapOf<T> = Map<keyof T, T[keyof T]>;
const createMap = <T,>(): MapOf<T> => Map<keyof T, T[keyof T]>();
For the dashboard:
export interface DashboardState {
componentId?: number;
dashboard?: number;
}
export default function (state = createMap<DashboardState>(), action): MapOf<DashboardState> {
switch (action.type) {
case dashboardsActionsTypes.SET_PROJECT_VIEW:
return state.set('componentId', action.payload);
case dashboardsActionsTypes.SET_DASHBOARD_TYPE:
return state.set('dashboard', action.payload);
default:
return state;
}
}
Upvotes: 2