Inferring mapped props when using TypeScript and React-Redux

Yes. There's a neat technique for inferring the type of the combined props that connect will pass to your component based on mapState and mapDispatch.

There is a new ConnectedProps<T> type that is available in @types/[email protected]. You can use it like this:

function mapStateToProps(state: MyState) {
    return {
        counter: state.counter
    };
}

const mapDispatch = {increment};

// Do the first half of the `connect()` call separately, 
// before declaring the component
const connector = connect(mapState, mapDispatch);

// Extract "the type of the props passed down by connect"
type PropsFromRedux = ConnectedProps<typeof connector>
// should be: {counter: number, increment: () => {type: "INCREMENT"}}, etc

// define combined props
type MyComponentProps = PropsFromRedux & PropsFromParent;

// Declare the component with the right props type
class MyComponent extends React.Component<MyComponentProps> {}

// Finish the connect call
export default connector(MyComponent)

Note that this correctly infers the type of thunk action creators included in mapDispatch if it's an object, whereas typeof mapDispatch does not.

We will add this to the official React-Redux docs as a recommended approach soon.

More details:

  • Gist: ConnectedProps - the missing TS helper for Redux
  • Practical TypeScript with React+Redux
  • DefinitelyTyped #31227: Connected component inference
  • DefinitelyTyped PR #37300: Add ConnectedProps type

I don't think so. You could make your setup more concise by using Redux hooks: https://react-redux.js.org/next/api/hooks

    // Your function component . You don't need to connect it
    const App: React.FC = () => {
      const counter = useSelector<number>((state: MyState) => state.counter);
      const dispatch = useDispatch(); // for dispatching actions
    };

Edit: You can if you just use the same MyState type. But I don't think you would want that.


I would type mapped dispatch props, and component props separately and then combine the inferred type of the mapped state to props function. See below for a quick example. There might be a more elegant solution but hopefully, it will hopefully get you on the right track.

import * as React from "react";
import { Action } from "redux";
import { connect } from "react-redux";

// Lives in some lib file
type AppState = {
  counter: number;
};

type MappedState = {
  computedValue: number;
};
type MappedDispatch = {
  doSomethingCool: () => Action;
};
type ComponentProps = {
  someProp: string;
};

const mapStateToProps = (state: AppState) => ({
  computedValue: state.counter
});

const mapDispatchToProps: MappedDispatch = {
  doSomethingCool: () => {
    return {
      type: "DO_SOMETHING_COOL"
    };
  }
};

type Props = ReturnType<typeof mapStateToProps> &
  MappedDispatch &
  ComponentProps;

class DumbComponent extends React.Component<Props> {
  render() {
    return (
      <div>
        <h1>{this.props.someProp}</h1>
        <div>{this.props.computedValue}</div>
        <button onClick={() => this.props.doSomethingCool()}>Click me</button>
      </div>
    );
  }
}

const SmartComponent = connect(
  mapStateToProps,
  mapDispatchToProps
)(DumbComponent);

export default SmartComponent;