12

I am trying to use react-redux with typescript and I'm getting a type error when I try to inject props using connect() and mapStateToProps.

My component looks like this:

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

function mapDispatchToProps(dispatch) {
    return {
        incr: () => {
            dispatch({type: 'INCR', by: 2});
        },
        decr: () => {
            dispatch({type: 'INCR', by: -1});
        }
    };
}




export default class Counter extends React.Component<HelloProps, any> {
    render() {
        return (
          <div>
              <p>
                  <label>Counter: </label>
                  <b>#{this.props.counter}</b>
              </p>
              <button onClick={e => this.props.incr() }>INCREMENT</button>
              <span style={{ padding: "0 5px" }}/>
              <button onClick={e => this.props.decr() }>DECREMENT</button>
        );
    }
}


export default connect(mapStateToProps, mapDispatchToProps)(Counter);

The store looks like this

let store = createStore(
    (state:HelloState, action:HelloAction) => {
        switch (action.type) {
            case 'INCR':
                return {counter: state.counter + action.by};
            default:
                return state;
        }
    },

Finally, I have defined my types to be:

interface HelloProps {
    counter?: number;
    incr?: () => any;
    decr?: () => any;
}

interface HelloState { 
    counter:number;
}

interface HelloAction { 
    type:string, 
    by:number;
}

When I try and compile the code I get the following error:

(39,61): error TS2345: Argument of type 'typeof Counter' is not assignable to parameter of type 'ComponentClass<{ counter: any; } & { incr: () => void; decr: () => void; }> | StatelessComponent<...'.
  Type 'typeof Counter' is not assignable to type 'StatelessComponent<{ counter: any; } & { incr: () => void; decr: () => void; }>'.
    Type 'typeof Counter' provides no match for the signature '(props?: { counter: any; } & { incr: () => void; decr: () => void; }, context?: any): ReactElement<any>' 

Interestingly the code still works even though it throws the type error. Also, changing the component's prop interface to any also solves the issue. It seems like the type system doesn't understand that the two objects are merged by the two mapped functions.

1
  • You are exporting the Counter Component twice, maybe that can also be an issue here. Commented Apr 24, 2018 at 12:33

2 Answers 2

40

To preserve type safety you can divide your props in parts, one responsible for normal props and one for dispatchers:

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

interface StateProps {
    textPros: string,
    optionalText?: string,

}
interface DispatchProps {
    onClick1: Function,

}

class MyComp extends React.Component<StateProps & DispatchProps , any> {
    render() {
         return (<div onClick={this.props.onClick1}>{this.props.textPros}</div>);
    }
}
const mapStateToProps = (state: any, ownProp? :any):StateProps  => ({
    textPros: "example text",
});
const mapDispatchToProps = (dispatch: any):DispatchProps => ({
    onClick1: () => {
        dispatch({ type: 'CLICK_ACTION'});
    }
});
export default connect(mapStateToProps, mapDispatchToProps)(MyComp);

For those who search for quick workaround: just add 'as any' to base component.

export default connect(mapStateToProps, mapDispatchToProps)(MyComp as any);
Sign up to request clarification or add additional context in comments.

4 Comments

In my case, I didn't have any values for mapDispatchToProps, so explicitly passing null or undefined instead of leaving off the value seemed to make the error go away: export default connect(mapStateToProps, null)(MyComponent);
as. as? as! \(^o^)/
This specifically fails as you need to type-parameterize connect: Even then, there seem to be deeper issues with connect.
What's the point to use TypeScript and then put any here and there? Bad example.
9

I found the answer in the second to last post on this Github issue. Without the type parameter on both the mapStateToProps and/or mapDispatchToProps or on connect it will produce an intersection of the return types of the two map functions.

2 Comments

did you found out anything more (explanation) about this? I noticed that you didn't receive any answer on your question on the github issue. Are you using return types for the mapStateToProps and mapDispatchToProps (for which you probably had to made the members of the interface optional like you suggested)? That is how I am currently applying it to be able to run.
Yeah I specified the return types on mapStateToProps and mapDispatchToProps. The interface members do have to be optional no matter what otherwise it will fail where the component is called. I'm not a huge fan of how TS works with Redux and react-redux's connect method but I can't think of a good way around that right now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.