I try to build a react app in typescript using redux and react-router-dom. I ran into typing issues when I added redux to my app. Thus I created the following minimal example with only one page test-page:
App.jsx
import * as React from 'react';
import { Route, Redirect } from 'react-router-dom'
import Test from './containers/test-page'
import './App.css';
class App extends React.Component {
render() {
return (
<div className="ui container" id="main">
<Route exact path="/" render={() => <Redirect to="/test" />}/>
<Route exact path="/test" component={Test} />
</div>
);
}
}
export default App;
The container for the test page looks like this. It produces a typing error in the call to connect.
containers/test-page/index.tsx
import { Dispatch } from 'redux'
import { connect } from 'react-redux'
import TestPage from './test-page'
function mapDispatchToProps(dispatch: Dispatch<any>) {
return dispatch({ type: 'ALERT_USER' });
}
function mapStateToProps(state: any) {
return { label: 'my test label' }
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TestPage)
The container uses the following react component, which in production should render a page for the router. It produces two errors, see below.
containers/test-page/test-page.tsx
import * as React from 'react';
export namespace Test {
export interface Props {
alert: () => void;
label: string;
}
export interface State {
}
}
export default class TestPage extends React.Component {
constructor(props?: Test.Props, state?: Test.State, context?: any) {
super(props, context);
}
sendAlert = () => {
this.props.alert()
}
render() {
return (
<div>
<h1>Test</h1>
<button onClick={this.sendAlert}>{this.props.label}</button>
</div>
);
}
}
proxyConsole.js:54 ./src/containers/test-page/test-page.tsx
(20,18): error TS2339: Property 'alert' does not exist on type 'Readonly<{ children?: ReactNode; }> & Readonly<{}>'.
proxyConsole.js:54 ./src/containers/test-page/test-page.tsx
(27,54): error TS2339: Property 'label' does not exist on type 'Readonly<{ children?: ReactNode; }> & Readonly<{}>'.
proxyConsole.js:54 ./src/containers/test-page/index.tsx
(16,3): error TS2345: Argument of type 'typeof TestPage' is not assignable to parameter of type 'ComponentType<{ label: string; } & { type: string; }>'.
Type 'typeof TestPage' is not assignable to type 'StatelessComponent<{ label: string; } & { type: string; }>'.
Type 'typeof TestPage' provides no match for the signature '(props: { label: string; } & { type: string; } & { children?: ReactNode; }, context?: any): ReactElement<any> | null'.
I tried to follow different guides and looked up example implementations but could not solve these issues. I do not understand the error messages of the typescript compiler:
this.props
when I defined them?A couple of things I notice:
1) As far as I've seen in examples and when working with props
in TypeScript, your call to React.Component
needs to specify Props
as a type argument like so:
export default class TestPage extends React.Component<Test.Props, Test.State>{
constructor(props: Test.Props) {
super(props);
}
}
You can specify that your component does not accept props
or state
by passing empty interfaces i.e.:
export default class TestPage extends React.Component<{}, {}>{
// constructor becomes 'useless' at this point and is not needed
constructor() {
super();
}
}
I think this explains why your call signature is not matching and why there are no properties visible on this.props
- TS sees an interface of ReadOnly{}
since it has no type arguments passed in.
2) Your mapStateToProps
function doesn't look quite right. mapStateToProps
takes two arguments, state
(referencing your Redux store
) and ownProps
as an optional second argument, which refers to props
passed down from the parent. So mapStateToProps
should look like this:
function mapStateToProps(state: any, ownProps: { label: string }) {
return {
label: ownProps.label
};
}
This is my guess for why connect
is throwing an error - it is simply a place where you make assertions about how Redux should handle combining props
coming from the store
and props
coming from the parent. Let me know if this works out.
This doesn't work because connect is a generic function. This means you need to provide additional type parameters.
connect<StateProps, DispatchProps>({
mapStateToProps,
mapDispatchToProps,
})(SomeComponent);
You can find typing implementation here. Everything you need to know is there C:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With