Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inferring mapped props when using TypeScript and React-Redux

I found a way to get type safety when using mapStateToProps from react-redux: as documented you can define an interface and parameterize React.Component<T> with your interface.

However, when I am defining mapStateToProps, I'm already defining a function where the types of the properties of the resulting object can be inferred. Eg,

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

Here, the prop counter can be inferred to be the same type as state.counter. But I still have to have boilerplate code like the following:

interface AppProps {
    counter: number;
}


class App extends React.Component<AppProps> { ... }

export default connect(mapStateToProps)(App);

So the question is, is there any way to structure the code so that I can avoid writing the type of counter twice? Or to avoid parameterizing the type of React.Component -- even if I could have the props of the component inferred from an explicitly-hinted result type of the mapStateToProps function, that would be preferable. I am wondering if the duplication above is indeed the normal way to write typed components using React-Redux.

like image 876
amoe Avatar asked Oct 30 '19 10:10

amoe


1 Answers

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
like image 129
markerikson Avatar answered Oct 22 '22 07:10

markerikson