I have a src/reducers/index.tsx
file where I output all my reducers:
import counterReducer from '../reducers/counter';
import loggedReducer from '../reducers/isLogged';
import {combineReducers} from 'redux';
const allReducers = combineReducers({
counter: counterReducer,
isLogged: loggedReducer,
});
export default allReducers;
Then in my src/index.tsx
:
import {Provider} from 'react-redux';
const store = createStore(allReducers);
ReactDOM.render(
<React.Fragment>
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);
And finally in my src/app.tsx
I have:
import {useSelector} from 'react-redux';
const App = () => {
const counter = useSelector(state => state.counter);
return (
<h1>counter {counter}</h1>
);
};
The error is reported in the state.counter
part of the useSelector
:
> Property 'counter' does not exist on type 'DefaultRootState'.ts(2339)
It looks like the store is never actually created?
The "Property does not exist on type ' {}'" error occurs when we try to access or set a property that is not contained in the object's type. To solve the error, type the object properties explicitly or use a type with variable key names. Copied!
We didn't explicitly type the obj variable and declared it to an empty object, so we aren't able to assign or access properties that don't exist on the object's type.
There's no DefaultRootState.ts the error refers to the DefaultRootState type, the . ts (2339) is just formatting. By defining a custom type for the state I resolved the error. Check answer :). Non-hacky answer inbound!
To fix the "Typescript Error: Property ‘user’ does not exist on type ‘Request’" error with Express, we can add our own definition for the request type. into a TypeScript type definition file. We create the IGetUserAuthInfoRequest interface the extends the Request interface by adding the user field.
Non-hacky answer inbound!
Link to Redux Toolkit docs for creating typed react-redux hooks: https://react-redux.js.org/using-react-redux/usage-with-typescript#define-root-state-and-dispatch-types
(While this is a link to the Redux Toolkit docs, I believe this technique should still work with "vanilla" react-redux
. Please someone correct me with a comment if I'm wrong.)
The useSelector
hook by itself is unaware of the typing that should be applied to the state you want to use. You'll have to do a couple lines of work to earn your type safety! Utilizing the technique shown in the link, you'll get to have the typings for your state every time you use it. You'll create a one-line custom hook that utilizes the native useSelector
hook with the inferred types from your root state to find out what your states' types are supposed to be.
The useSelector
hook by itself is unaware of the typing that should be applied to the state you want to use. To make it so that the types of your store get captured and applied to your useSelector
hooks, you'll need to jump through two small hoops. We're also going to take care of your useDispatch
hook while we're at it.
Let's grab the inferred types from the store. To do this, we'll go to where our store is created and use some Typescript magic to get the inferred types from the state:
// Copied from Redux Toolkit docs that are linked above
// store.ts
import rootReducer from './rootReducer'
const store = createStore(rootReducer)
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
Brief pause to unpack lines 6 and 7:
Line 6: export type RootState = ReturnType<typeof store.getState>
ReturnType
means "The return type of the function."
typeof
means "Tell me what the types are of this thing."
store.getState
is a function that returns the state object that is currently in the store.
Now put those pieces together!: Whatever is being returned from store.getState, I want the types stored as the RootState
variable!
Line 7: export type AppDispatch = typeof store.dispatch
typeof
means "Tell me what the types are of this thing."store.dispatch
means "I want the dispatch object used to dispatch actions to the store."Now put those pieces together!: Get the dispatch object from the store, break it down into it's types, and store it in the AppDispatch
variable!
Now that we have the inferred types from our store and dispatch, we can apply them to our hooks. We will do this by creating a custom hook so that we don't have to handle typing our hooks every time we go to use them:
// Copied from Redux Toolkit docs that are linked above
// hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './store'
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
Here, we are taking the types that we created in store.ts
and applying them to the useDispatch
and useSelector
hooks that we normally use in react-redux
. From now on, you'll have your types set up for you when you use your new custom hooks!
Voila! You now have type safety in your react-redux hook implementation!
This works for me.
import { RootStateOrAny, useSelector } from 'react-redux';
const { domain } = useSelector(
(state: RootStateOrAny) => state.currentSite?.site,
);
It was indeed a TypeScript issue. There were a few things I changed to get my app to compile and also to work with Redux. I know we shouldn't post links but https://github.com/piotrwitek/react-redux-typescript-guide this is a great resource! Based on the examples there I fixed my issues:
I changed the useSelector to define a type to the state, this resolved the original error.
import {RootState} from 'typesafe-actions';
const counter = useSelector((state: RootState) => state.counter);
But what is RootState
? RootState is a custom type created in the src/store/types.d.ts
file:
import {StateType, ActionType} from 'typesafe-actions';
declare module 'typesafe-actions' {
export type Store = StateType<typeof import('./index').default>;
export type RootState = StateType<typeof import('./root-reducer').default>;
export type RootAction = ActionType<typeof import('./root-action').default>;
interface Types {
RootAction: RootAction;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With