Search code examples
reactjstypescriptredux

Property 'y' does not exist on type 'DefaultRootState'


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?


Solution

  • Non-hacky answer inbound!

    Shortest Answer:

    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.)

    Short Answer:

    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.

    Longer Answer:

    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.

    Step 1

    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!

    Step 2

    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!