Search code examples
reactjstypescriptreduxtyping

Typing mapStateToProps React Redux


I am not able to type the "state" parameter of a mapStateToProps

If I just change state : any instead of state: AppState it works and no error. But I would like to have a correct typing for my state parameter.

For now, I have this error on the mapStateToProps param of the connect()

No overload matches this call. The last overload gave the following error. Argument of type '(state: { quiz: IQuizInitialState; }) => StateProps' is no assignable to parameter of type 'MapStateToPropsParam'. Cannot assign the type '(state: { quiz: IQuizInitialState; }) => StateProps' to type 'MapStateToPropsFactory'. Parameters 'state' and 'initialState' are not compatible. Property 'quiz' is missing in type '{}' but required in type '{ quiz: IQuizInitialState; }'.ts(2769)

interface OwnProps {

}
interface StateProps {

}
interface DispatchProps {

}

type Props = OwnProps & StateProps & DispatchProps;


export class App extends Component<Props> {

  render() {
    return (
     <div>Hey</div>
    );
  }
}

const mapStateToProps = (state: AppState): StateProps => ({ 
})

const mapDispatchToProps = (dispatch: ThunkDispatch<{}, {}, AnyAction>): DispatchProps => {
    return {
    }
}


// The args 'mapStateToProps' generate the error
export default connect<StateProps,DispatchProps,OwnProps>(mapStateToProps, mapDispatchToProps)(App)

This is my rootReducer :

import { combineReducers } from 'redux';
import { QuizReducer } from './quiz';

const rootReducer = combineReducers({
    quiz: QuizReducer
});

export type AppState = ReturnType<typeof rootReducer>


export default rootReducer;

And the single reducer is :

import { TYPES } from '../actions/action-types';
import { IQuizListItem, Action } from '../models/index';
import { AnyAction } from 'redux';


export interface IQuizInitialState {
    quizListItem: IQuizListItem[]
}
const quizInitialState: IQuizInitialState = {
    quizListItem: []
}
export const QuizReducer = (state = quizInitialState, action: AnyAction): IQuizInitialState => {
    switch (action.type) {
        case TYPES.getQuizListItems:
            return {
                ...state,
                quizListItem: (action as Action<IQuizListItem[]>).payload
            }

        default:
            return state
    }
}

Thank you by advance guys !


Solution

  • The type of your state is the same type you use for the whole state. since mapStateToProps takes the whole state to pass it down to selectors. in your case I believe this would be the correct type IQuizInitialState.

    const mapStateToProps = (state: IQuizInitialState): StateProps => ({ 
    
    })
    

    EDIT

    In your comment you mention IQuizInitialState isnt your whole application state. Then that one is not the one you need. You need a type for the whole application state. To achieve that you could create an interface for every single reducer type meaning your IQuizInitialState but for the other reducers into a single interface.

    Ill have to asume here since I dont have your code base but consider

    combineReducers({potato: quizReducer, tomato: otherReduzer})
    

    you'll need a type

    interface IApplicationState {
    potato: IQuizInitialState;
    tomato: IOTherInterfaceYouDefinedForThisReducer;
    }
    

    your combineReducers will probable look like :

    combineReducers<IApplicationState>({
      potato: quizReducer,
      tomato: otherReduzer
    });
    

    I hope you get the idea.

    EDIT 2 After reading your last comment I noticed you are asking for the mapStateToProps with two arguments. and you are just defining one. Your connect generics seems wrong then. you should consider the following:

    connect<StateProps, DispatchProps, Props, IApplicationState>
    

    where:

    • StateProps : describes what was returned by mapStateToProps()
    • DispatchProps: describes what is returned by dispatchToProps()
    • Props: Your component props
    • IApplicationState: Represents your Apps Redux whole state