I am trying to get a react-redux app going with typescript, but I keep circling around the same error. The following code compiles and produces expected result
// State definition
interface HelloWorldState {
clickCount: number
}
interface AppState extends HelloWorldState {}
// Props definitions
interface HelloWorldProps {
count: number
}
// Actions
const CLICK = 'CLICK';
const click = () => {return {type:CLICK}};
// Reducers
function clickCount(state:number = 0, action:Action) {
if (typeof state === 'undefined') {
return 0;
}
switch (action.type) {
case CLICK:
return state + 1;
default:
return state;
}
}
let rootReducer = combineReducers({
clickCount
});
// Store
let store = createStore(rootReducer);
// Components
class HelloWorld extends React.Component<any, any> {
render() {
return <div onClick={this.handleClick.bind(this)}>Hello world "{this.props.count}"</div>
}
handleClick() {
store.dispatch(click())
}
}
// Container components
const mapStateToProps = (state:AppState):HelloWorldState => {
return Immutable.fromJS({
count: state.clickCount
})
};
const ConnectedHelloWorld = connect(
mapStateToProps
)(HelloWorld);
render(
<Provider store={store}>
<ConnectedHelloWorld/>
</Provider>,
container
);
Great! But I am using TypeScript, because I want to get type checks at compile time. The most important thing to type check is state and props. So instead of class HelloWorld extends React.Component<any, any>, I want to do class HelloWorld extends React.Component<HelloWorldProps, any>. When I do this, however, I get the following compile error from the call to render
TS2324:Property 'count' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & HelloWorldProps & { children?: React...'.
I don't really understand why. count IS present in the HelloWordProps definition, and it is provided by the reducer, so I should be fine, right? Similar questions has suggested that it is an inference problem, and that I should declare the type of the call to connect, but I can't seem to find out how
package.json
{
"name": "reacttsx",
"scripts": {
"build": "webpack"
},
"devDependencies": {
"ts-loader": "^1.3.3",
"typescript": "^2.1.5",
"webpack": "^1.14.0",
"typings": "^2.1.0"
},
"dependencies": {
"es6-promise": "^4.0.5",
"flux": "^3.1.2",
"immutable": "^3.8.1",
"isomorphic-fetch": "^2.2.1",
"jquery": "^3.1.1",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react-redux": "^5.0.2",
"redux": "^3.6.0",
"redux-logger": "^2.7.4",
"redux-thunk": "^2.2.0"
}
}
typings.json
{
"dependencies": {
"flux": "registry:npm/flux#2.1.1+20160601175240",
"immutable": "registry:npm/immutable#3.7.6+20160411060006",
"react": "registry:npm/react#15.0.1+20170104200836",
"react-dom": "registry:npm/react-dom#15.0.1+20160826174104",
"react-redux": "registry:npm/react-redux#4.4.0+20160614222153",
"redux-logger": "registry:dt/redux-logger#2.6.0+20160726205300",
"redux-thunk": "registry:npm/redux-thunk#2.0.0+20160525185520"
},
"globalDependencies": {
"es6-promise": "registry:dt/es6-promise#0.0.0+20160726191732",
"isomorphic-fetch": "registry:dt/isomorphic-fetch#0.0.0+20170120045107",
"jquery": "registry:dt/jquery#1.10.0+20170104155652",
"redux": "registry:dt/redux#3.5.2+20160703092728",
"redux-thunk": "registry:dt/redux-thunk#2.1.0+20160703120921"
}
}
UPDATE
Since it was complaining that count was missing, I tried updating to
render(
<Provider store={store}>
<ConnectedHelloWorld count={0}/>
</Provider>,
container
);
This solves the issue. So the issue is that the compiler doesn't know that the Provider is providing the count. Provider uses the store. The store should have the clickCount value which is mapped to count by the container component.
I noticed I'd forgotten an initial state for the store. So even if the types had checked out, the state would be empty. I updated it to
// Store
let initialState:AppState = {clickCount: 0};
let store = createStore(rootReducer, initialState);
Now I am certain that clickCount is set properly in the store. So I'd expect the mapStateToProps function to take the AppState and return the HelloWorldProps as specified, and then the Provider should provide the count value. This is true, but the compiler does not see it.
So how to remedy that?
HelloWorldclass? (that is, without the redux) Because it works for me. Also, what version of react are you using?HelloWorld extends React.Component<any, any>? Yes, that works always, because the types of its state and props areany. So no error there.class HelloWorld extends React.Component<HelloWorldProps, any>without the redux stuff. For me that compiles just fine. What version of react/typescript are you using?connect()the component to the store. We also exportMappedPropstype to be able to use it as props type in the component.