Open ricosandyca opened 4 years ago
I also have this error. Changing "strict" to false in tsconfig.json works, but i don't want to disable it.
Hello, Don't know if I should put it here or in new issue. Similar error is found when using persistCombineReducers
Had same issue. There was an untyped object I had missed. I set typescript strict to false which found the object for me, was able to fix the object and set typescript strict back to true again. Hope that helps.
My fix may not be so relevant, but this question at least led me to a solution so I'll post it in the off chance someone with the same problem as I comes across this.
I just had to type persistReducer. For example:
persistReducer<RootState>(persistConfig, rootReducer());
1085
This PR is solution of this error. Owner of repo kindly merges this PR.
I came to this issue from Google about this TypeScript issue:
If I have this rootReducer
:
export const rootReducer = combineReducers({
user: userReducer,
});
and try to use
const persistedReducer = persistReducer(persistConfig, rootReducer);
I will get the following TypeScript error on rootReducer
:
Argument of type 'Reducer<CombinedState<{ user: UserState; }>, AnyAction>' is not assignable to parameter of type 'Reducer<unknown, AnyAction>'.
Types of parameters 'state' and 'state' are incompatible.
Type 'unknown' is not assignable to type 'CombinedState<{ user: UserState; }> | undefined'.
Type 'unknown' is not assignable to type 'CombinedState<{ user: UserState; }>'.
Type 'unknown' is not assignable to type '{ readonly [$CombinedState]?: undefined; }'.ts(2345)
possibly because combineReducers
added the readonly [$CombinedState]?: undefined; }
type to the rootReducer
but wasn't type-checked by persistReducer
.
For now, I resolved it by adding the RootState
as type parameter:
const persistedReducer = persistReducer<RootState>(persistConfig, rootReducer);
But I hope that the PR #1085 above could possibly resolve this one as well, such that I don't need to manually fill the RootState
type. Or maybe a section such as "Troubleshooting" or "Usage with Typescript" could be added to redux-persist
README regarding some common issues such as this one and usage together with redux-toolkit
.
any update?
I fixed with:
const persistedReducer = persistReducer<any, any>(persistConfig, RootReducer);
This PR is solution of this error.
// redux-persist.d.ts
declare module 'redux-persist/es/persistStore' {
import { Store, Action, AnyAction } from 'redux';
import { PersistorOptions, Persistor } from 'redux-persist/es/types';
// tslint:disable-next-line: strict-export-declare-modifiers
export default function persistStore<S = any, A extends Action<any> = AnyAction>(
store: Store<S, A>,
persistorOptions?: PersistorOptions | null,
callback?: () => any,
): Persistor;
}
declare module 'redux-persist/lib/persistStore' {
export * from 'redux-persist/es/persistStore';
export { default } from 'redux-persist/es/persistStore';
}
Any update for this issue?
I had to adjust my createStore into another function named create like this:
import {
EmptyObject,
applyMiddleware,
createStore,
Middleware,
Reducer,
} from 'redux'
import createSagaMiddleware from 'redux-saga'
import { persistStore, persistReducer } from 'redux-persist'
import { PersistPartial } from 'redux-persist/lib/persistReducer'
import { UserAction, UserState } from './modules/user/types'
import rootReducer from './modules/rootReducer'
import rootSaga from './modules/rootSaga'
export interface StoreState {
user: UserState // the type of your reducer
}
const create = (
reducers: Reducer<EmptyObject & StoreState & PersistPartial, UserAction>,
middlewares: Middleware[],
) => {
const enhancer = applyMiddleware(...middlewares)
return createStore(reducers, enhancer)
}
const persistConfig = {
key: 'root',
storage,
}
const sagaMiddleware = createSagaMiddleware()
const middlewares: Middleware[] = [sagaMiddleware]
const persistedReducer = persistReducer(persistConfig, rootReducer)
// here is the trick instead of createStore I use my modified create function
const store = create(persistedReducer, middlewares)
sagaMiddleware.run(rootSaga)
export const persistor = persistStore(store)
export default store
I suppose you can easily ignore the middleware part if you don't need it:
And Inside of your App component you can use:
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<GlobalStyle />
<Routes />
</PersistGate>
</Provider>
const persistedReducer = persistReducer<RootState, any>(persistConfig, rootReducer)
I had to adjust my createStore into another function named create like this:
import { EmptyObject, applyMiddleware, createStore, Middleware, Reducer, } from 'redux' import createSagaMiddleware from 'redux-saga' import { persistStore, persistReducer } from 'redux-persist' import { PersistPartial } from 'redux-persist/lib/persistReducer' import { UserAction, UserState } from './modules/user/types' import rootReducer from './modules/rootReducer' import rootSaga from './modules/rootSaga' export interface StoreState { user: UserState // the type of your reducer } const create = ( reducers: Reducer<EmptyObject & StoreState & PersistPartial, UserAction>, middlewares: Middleware[], ) => { const enhancer = applyMiddleware(...middlewares) return createStore(reducers, enhancer) } const persistConfig = { key: 'root', storage, } const sagaMiddleware = createSagaMiddleware() const middlewares: Middleware[] = [sagaMiddleware] const persistedReducer = persistReducer(persistConfig, rootReducer) // here is the trick instead of createStore I use my modified create function const store = create(persistedReducer, middlewares) sagaMiddleware.run(rootSaga) export const persistor = persistStore(store) export default store
I suppose you can easily ignore the middleware part if you don't need it:
And Inside of your App component you can use:
<Provider store={store}> <PersistGate loading={null} persistor={persistor}> <GlobalStyle /> <Routes /> </PersistGate> </Provider>
@rayonnunes Do you know since what version it's available EmptyObjet from redux?
I can't find it.
Thanks.
1278
declare module 'redux-persist/es/persistStore' { import { Store, Action, AnyAction } from 'redux'; import { PersistorOptions, Persistor } from 'redux-persist/es/types'; // tslint:disable-next-line: strict-export-declare-modifiers export default function persistStore<S = any, A extends Action<any> = AnyAction>( store: Store<S, A>, persistorOptions?: PersistorOptions | null, callback?: () => any, ): Persistor; } declare module 'redux-persist/lib/persistStore' { export * from 'redux-persist/es/persistStore'; export { default } from 'redux-persist/es/persistStore'; }
This is the best solution, redux-persist should fix this as suggested in this code snippet
You need to change the type of the persistedReducer to <reducer type> & PersistPartial
Example:
If this is the reducer you want to persist.
const rootReducer = combineReducers<RootState>({
auth: persistReducer(authPersistConfig, AuthReducer)
});
In RootState, auth property should have this type.
import { PersistPartial } from 'redux-persist/es/persistReducer';
export interface RootState {
auth: AuthState & PersistPartial;
}
This worked in my case. creating a type RootReducer and add persistReducer< RootReducer >. This example uses RTK query, redux toolkit and redux-persist, and redux-persist-transform-encrypt
rootReducer.ts
import { combineReducers } from '@reduxjs/toolkit';
import { reducer as settingsReducer } from 'src/redux/slices/settings';
import { loginApi } from 'src/services/loginService';
import { nluApi } from 'src/services/nluService';
const appReducer = combineReducers({
settings: settingsReducer,
[loginApi.reducerPath]: loginApi.reducer,
[nluApi.reducerPath]: nluApi.reducer
});
const rootReducer = (state, action) => {
if (action.type === 'RESET_APP') {
state = undefined;
}
return appReducer(state, action);
};
export const resetAppAction = () => (dispatch) => {
dispatch({ type: 'RESET_APP' });
};
export type RootReducer = ReturnType<typeof rootReducer>;
export default rootReducer;
index.ts
import { useDispatch as useReduxDispatch, useSelector as useReduxSelector } from 'react-redux';
import type { TypedUseSelectorHook } from 'react-redux';
import type { ThunkAction } from 'redux-thunk';
import type { Action } from '@reduxjs/toolkit';
import { configureStore } from '@reduxjs/toolkit';
import { setupListeners } from '@reduxjs/toolkit/query/react';
import storage from 'redux-persist/lib/storage';
import {
persistStore,
persistReducer,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from 'redux-persist';
import { encryptTransform } from 'redux-persist-transform-encrypt';
import thunk from 'redux-thunk';
import rootReducer, { RootReducer } from './rootReducer';
import { loginApi } from 'src/services/loginService';
import { nluApi } from 'src/services/nluService';
const encryptor = encryptTransform({
secretKey: process.env.REACT_APP_REDUX_SECRET_KEY,
onError(error) {
// Handle the error.
},
});
const persistConfig = {
// Root
key: 'root',
storage,
timeout: null,
blacklist: [
loginApi.reducerPath,
nluApi.reducerPath
],
transforms: [encryptor]
};
const persistedReducer = persistReducer<RootReducer>(persistConfig, rootReducer);
export const store = configureStore({
reducer: persistedReducer,
devTools: process.env.NODE_ENV === 'development',
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}).concat(
thunk,
loginApi.middleware,
nluApi.middleware
),
});
// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch);
export const persistor = persistStore(store);
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk = ThunkAction<void, RootState, null, Action<string>>;
export const useSelector: TypedUseSelectorHook<RootState> = useReduxSelector;
export const useDispatch = () => useReduxDispatch<AppDispatch>();
if you are using redux/toolkit and are getting this error or losing typing on your selectors then use the following configuration:
store.ts
const rootReducer = combineReducers({
user: userReducer,
})
const persistedReducer = persistReducer(
persistConfig,
rootReducer
) as typeof rootReducer
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
})
})
export const persistor = persistStore(store)
export type AppDispatch = typeof store.dispatch
export type RootState = ReturnType<typeof store.getState>
@TimCrooker Thanks you. It works for me
Much better to use the real typings with persistReducer's generics persistReducer<RootReducer, AnyAction>
export const rootReducer = combineReducers({
user: userReducer,
});
export type RootReducer = ReturnType<typeof rootReducer>;
const persistedReducer = persistReducer<RootReducer, AnyAction>(
{
key: 'root',
storage,
stateReconciler: hardSet,
},
rootReducer,
);
export const store = configureStore({
reducer: persistedReducer,
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
serializableCheck: false,
}).concat([]),
});
Anyone here can give me a simple implementations of a redux-persist on typescript? I always got error like this when i try to create persistor
Argument of type 'Store<{ readonly [$CombinedState]?: undefined; } & { todo: TodoState; } & PersistPartial, TodoActionTypes>' is not assignable to parameter of type 'Store<any, AnyAction>'. Types of property 'dispatch' are incompatible. Type 'Dispatch<TodoActionTypes>' is not assignable to type 'Dispatch<AnyAction>'. Type 'AnyAction' is not assignable to type 'TodoActionTypes'. Type 'AnyAction' is not assignable to type 'DeleteTodoAction'.
This my code
Thanks