Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

567
Views
TS2339: Property 'tsReducer' does not exist on type 'DefaultRootState'

Struggling with the above question. Seen similar questions but cannot figure it out.

The below code is me attempting to open and close a dialog using TypeScript for first time in an existing React project which uses .js and .jsx.

import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import {useDispatch, useSelector} from 'react-redux';
import {closeTsDialog} from '../actions/tsDialog'
import {ActionTypes} from '../actions/types';

const TsApp = (): JSX.Element => {
    const dispatch = useDispatch();

// ERROR SHOWS UP ON LINE BELOW "state?.tsReducer?.isDialogOpen"
    const isDialogOpen = useSelector(state => state?.tsReducer?.isDialogOpen);
    const state = useSelector(s => s);
    console.log('->>>>>> state', state);


    // main tsx excluded to allow for posting on stackoverflow
};


export default TsApp;
import {TsDialogAction} from "../actions/tsDialog";


const initialState = {
    id: 0,
    isDialogOpen: false
};

const tsReducer = (state: TsDialogAction = initialState, action: Action) => {
    switch (action.type) {
        case ActionTypes.closeDialog: {
            return {...state, isDialogOpen: false};
        }
        case ActionTypes.openDialog: {
            return {...state, isDialogOpen: true};
        }
        default:
            return state;
    }
};

export default tsReducer;

import {ActionTypes} from './types';

export interface TsDialogAction { isDialogOpen: boolean number: number }

export interface CloseTsDialog { type: ActionTypes.closeDialog payload: TsDialogAction }

export interface OpenTsDialog { type: ActionTypes.openDialog payload: TsDialogAction }

export interface Increment { type: ActionTypes.increment payload: TsDialogAction }

export interface Decrement { type: ActionTypes.decrement payload: TsDialogAction }

export const closeTsDialog = (id: number) => ({type: ActionTypes.closeDialog, payload: id}); export const openTsDialog = (id: number) => ({type: ActionTypes.openDialog, payload: id}); export const incrementAction = (id: number) => ({type: ActionTypes.increment, payload: id}); export const decrementAction = (id: number) => ({type: ActionTypes.decrement, payload: id});

over 4 years ago · Santiago Trujillo
7 answers
Answer question

0

You need to declare the type of the state argument in your selector, like:

const isDialogOpen = useSelector( (state: RootState) => state.tsReducer.isDialogOpen);

Please see the Redux docs on TypeScript usage, as well as the React-Redux docs page on static typing for examples.

(Also, as a stylistic note: please don't call that tsReducer in your root state. Give it a name that matches the data it's handling, like state.ui.)

over 4 years ago · Santiago Trujillo Report

0

It is complaining about the type. Quick solution would be adding any as state type.

Proper solution will require following two steps:

  1. Create RootState type in Root Reducer.
export const rootReducer = combineReducers({
  dashboard: dashboardReducer,
  user: userReducer
});

export type RootState = ReturnType<typeof rootReducer>
  1. Provide RootState type to state object.
  let userData = useSelector((state: RootState) => {
    return state.user.data;
  });
over 4 years ago · Santiago Trujillo Report

0

For me, a better solution than specifying state in useSelector would be as below.
As in the node_modules/@types/react-redux/index.d.ts, you can use module augmentation.

/**
 * This interface can be augmented by users to add default types for the root state when
 * using `react-redux`.
 * Use module augmentation to append your own type definition in a your_custom_type.d.ts file.
 * https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
 */
// tslint:disable-next-line:no-empty-interface
export interface DefaultRootState {}

Do as below

  1. Export AppState in reducer src/reducer/index.ts
const reducers = combineReducers({
  userReducer,
});

export type AppState = ReturnType<typeof reducers>;
  1. Create new your_custom_type.d.ts. (I prefer react-redux.d.ts).
    In src/@types/your_custom_type.d.ts
import 'react-redux';

import { AppState } from '../reducers';

declare module 'react-redux' {
  interface DefaultRootState extends AppState { };
}
  1. Add typeRoots in tsconfig.json
{
  "compilerOptions": {
    ...
    "typeRoots": ["src/@types"]
  }
}

You can use as below without specifying AppState

import React, { memo } from 'react';
import { useSelector } from 'react-redux';

export default memo(() => {
  const isLoggedIn = useSelector(
    ({ userReducer }) => userReducer.isLoggedIn
  );
  return <div>{isLoggedIn}</div>;
});
over 4 years ago · Santiago Trujillo Report

0

If you are using react-redux, another out of the box solution would be to use RootStateOrAny.

import { RootStateOrAny, useSelector } from 'react-redux';

// and then use it like so in your component
...
const authState = useSelector((state: RootStateOrAny) => state.auth);
...


over 4 years ago · Santiago Trujillo Report

0

These are useful articles.

  1. https://redux.js.org/recipes/usage-with-typescript#define-root-state-and-dispatch-types
  2. https://redux.js.org/recipes/usage-with-typescript#define-typed-hooks

So first define RootState and AppDispatch like following:

//at store/index.ts
const rootReducer = combineReducers({
  tsReducer: tsReducer
});
const store = createStore(rootReducer)

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

And then define hooks (useAppDispatch, useAppSelector) can be used in components.

//at store/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './'

export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

And use it on component like following:

import {useAppSelector} from '../store/hooks'
//...
const TsApp = (): JSX.Element => {
    const dispatch = useDispatch();

// ERROR should be fixed
    const isDialogOpen = useAppSelector(state => state.tsReducer.isDialogOpen);
};
over 4 years ago · Santiago Trujillo Report

0

this worked for me

import { RootStateOrAny, useSelector } from "react-redux"

const search = useSelector((state: RootStateOrAny) => state.searchObj.value)
over 4 years ago · Santiago Trujillo Report

0

it is just about types that you need to satisfy to this hook, useSelector accepts DefaultRootState type, you can override its default types through this

type User = {
  firstname: string;
  lastname: string;
};
type SelectorTypes = {
  users: User[];
  //...other reducers
};
const router = useSelector<SelectorTypes>((state) => state.users);
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!