@types/xxxx
package and had problems.Definitions by:
in index.d.ts
) so they can respond.Even a very basic use of connect
on a React component ends in type errors. Reduced example:
import * as React from 'react';
import {connect, DispatchProp} from 'react-redux';
interface Props {
thing: string;
}
const mapStateToProps = (state) => ({
thing: 'abc'
});
@connect(mapStateToProps)
class Test extends React.Component<Props & DispatchProp<any>, void> {
render() {
return (<div>{this.props.thing}</div>);
}
}
On typescript 2.2.2 and below that type checks fine, but move to 2.3 or 2.4 and it results in cryptic error:
Argument of type 'typeof Test' is not assignable to parameter of type 'Component<DispatchProp<any> & { thing: string; }>'.
Type 'typeof Test' is not assignable to type 'StatelessComponent<DispatchProp<any> & { thing: string; }>'.
Type 'typeof Test' provides no match for the signature '(props: DispatchProp<any> & { thing: string; } & { children?: ReactNode; }, context?: any): ReactElement<any>'.
This is with @types/[email protected]
and @types/[email protected]
fwiw, I get a slightly more useful error by omitting the second generic type parameter:
- class Test extends React.Component<Props & DispatchProp<any>, void> {
+ class Test extends React.Component<Props & DispatchProp<any>> {
(I believe it's defaulted to {}
for state.)
The new error:
Unable to resolve signature of class decorator when called as an expression.
Type 'ComponentClass<{}>' is not assignable to type 'typeof Test'.
Type 'Component<{}, ComponentState>' is not assignable to type 'Test'.
Types of property 'render' are incompatible.
Type '() => false | Element' is not assignable to type '() => Element'.
Type 'false | Element' is not assignable to type 'Element'.
Type 'false' is not assignable to type 'Element'.
I don't know whether it's the same problem or not, but I have a very similar error messages which are caused by types/react-redux's index.d.ts:23, where changing Component<TOwnProps & TMergedProps>
to Component<TMergedProps>
in ComponentDecorator interface solves my problem.
As far as I know it's not a requirement for components to have TOwnProps as properties.
I have the same issue. My component does not extend DispatchProp<any>
, however. In the case of only having __mapStateToProps__, I don't think we should extend DispatchProp<any>
.
I think there is a bug in the type def file. If instead of ComponentDecorator<DispatchProp<any> & TStateProps, TOwnProps>
, we use ComponentDecorator<TStateProps, TOwnProps>
in line 57, the error goes away.
After some research, I found this post https://github.com/DefinitelyTyped/DefinitelyTyped/issues/6237. If you use connect function to generate HOC, you could specify some generic types which will solve the problem.
But regardless, I don't think we should let the component call dispatch directly. The component should not have any idea of whether it is using redux store. It should be the _container's_ job to pass down the functions to dispatch relevant actions.
header of @types/react-redux/index.d.ts
// Type definitions for react-redux 4.4.0
...
// TypeScript Version: 2.1
looks like it's time for "minor" update :)
Having the same issue as @ruszki, with the same fix working.
I ran into this issue yesterday and found what appears to be the solution. You need to include the dispatch method and any mapped props added through the connect method to the props interface on your component. They also can not be optional properties.
I used to write components like this below which now gives the errors in this thread. You would get similar errors if you omit the dispatch or mapped props.
export interface AppProps {
dispatch?: (action: any) => void;
appStateThing?: StatePart;
}
class App extends React.Component<AppProps, {}> {
render() {
return <div>I am an App</div>;
}
}
export default connect(s => {
return {
appStateThing: s.someStatePart,
};
})(App);
....
<Provider state={state}>
<App />
</Provider>
The solution is to change your props interface.
export interface AppProps {
dispatch: (action: any) => void;
appStateThing: StatePart;
}
Rather than being forced to define dispatch in your props
, you can supply null
as the mapDispatchToProps
function. If you don't need dispatch, then this can clear up some clutter.
export default connect(mapStateToProps, null)(Component);
@archy-bold why do you specify null
as the second argument?
Instead of:
export default connect(mapStateToProps)(Component);
@thasner If you don't then the definitions require your Component props include DispatchProp<S>
because if you don't specify a mapDispatchToProps
function connect
does the default which is to pass the dispatch
function directly to your component.
The problem I was experiencing was fixed recently here: https://github.com/DefinitelyTyped/DefinitelyTyped/commit/7353fa85191845aee8c504ab1e547ff539910f23#diff-e17ae37682234ef10ed127a622634fcb, which I think solves your issue @Archcry.
@stephen, Wait, am I being randomly tagged here?
Sorry, meant to tag @archy-bold.
changing the type definition from
interface ComponentDecorator<TMergedProps, TOwnProps> {
(component: Component<TOwnProps & TMergedProps>): ComponentClass<TOwnProps>;
}
to
interface ComponentDecorator<TMergedProps, TOwnProps> {
(component: Component<TMergedProps>): ComponentClass<TOwnProps>;
}
fixes my issues since I don't care about the container props passing into the component.
@codecorsair 's solution fixed it
My solution is to change the render function return type to React.ReacNode
@connect(mapStateToProps, mapDispatchToProps)
export class Container extends React.Component<IProps, {}> {
public render() {
return (<div></div>) as React.ReactNode;
}
}
I could only get this working by splitting everything out, its a bit verbose but there are types for everything I guess!
export interface IMergedProps {
aProp: string;
}
export interface IStateProps {
}
export interface IDipatchProps {
load?: (ref: string) => void;
}
export interface IState {
}
class Viewer extends React.Component<IMergedProps & IStateProps & IDipatchProps, IState> {
constructor(props: IMergedProps, context?: any) {
super(props, context);
}
componentDidMount() {
if (this.props.load) {
this.props.load(this.props.aProp);
}
}
render() {
return (
<div />
);
}
}
function mapStateToProps(state: IRootState): IStateProps {
return {
};
}
const mapDispatchToProps = (dispatch: Dispatch<any>): IDipatchProps => {
return {
load: (aProp: string) => { dispatch(actionCreators.search(aProp)); },
};
};
export const ConnectedViewer = connect<IStateProps, IDipatchProps, IMergedProps, IRootState>(
mapStateToProps,
mapDispatchToProps
)(Viewer);
Well that is a ridiculous solution. What is going on that will make me have to update 200+ files with boilerplate like this, just for updating Typescript and installing the Redux types? Anyone else think this is asinine?
@shudson250 I have another solution which seems to work, use any
instead of void
for actions on the component's props interface. Then it matches the signature.
e.g.:
export interface MyComponentProps {
someStateData: string;
onSomeAction: (text: string) => any;
}
@AndyBan when i take your example and add one line at the bottom:
const verify = <ConnectedViewer />
i get the error:
[ts]
Type '{}' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<Pick<IMergedProps, "aProp"> & IMergedPro...'.
Type '{}' is not assignable to type 'Readonly<Pick<IMergedProps, "aProp"> & IMergedProps>'.
Property 'aProp' is missing in type '{}'.
[ts]
JSX element type 'Component<Pick<IMergedProps, "aProp"> & IMergedProps, ComponentState>' is not a constructor function for JSX elements.
Types of property 'render' are incompatible.
Type '() => ReactNode' is not assignable to type '{ (): ReactNode; (): false | Element | null; }'.
Type 'ReactNode' is not assignable to type 'false | Element | null'.
Type 'undefined' is not assignable to type 'false | Element | null'.
const y.ConnectedViewer: React.ComponentClass<Pick<IMergedProps, "aProp"> & IMergedProps> & {
WrappedComponent: React.ComponentType<IMergedProps>;
}
using latest react, typescript and react-redux.
@ch1ll0ut1 Yeah aProp is a requred attribute, you could suffix it with a ? or add it to the def:
const verify = <ConnectedViewer aProp="a" />;
I came across this problem in my code and after playing around found that making the props non-optional fixed the problem, just as codecorsair found.
But the props marked as optional could be undefined, so making them non-optional makes the typings incorrect, which is a problem in of itself.
I ended up with a solution similar to AndyBan.
It isn't quite as verbose, but it's still sub-optimal.
//Component.tsx
export interface StateProps {
returnPath: string;
user?: User;
}
export interface DispatchProps {
loadUser: (userId: string) => any;
}
export interface Props extends StateProps, DispatchProps { }
export class Component extends React.Component<Props> {
...
}
//Container.tsx
export const Container = ReactRedux.connect(
(state: Store.ApplicationState, ownProps: OwnProps): Component.StateProps => ({
returnPath: ownProps.returnPath,
user: undefined,
}),
(dispatch: Store.Dispatch) => Redux.bindActionCreators({
loadUser: Store.User.actionCreators.loadUser
}, dispatch)
)(Component.Component);
Splitting out the Props adds a little bit of extra code in the component, and then just one extra type annotation in the Container.
You don't have to split things out like this unless you encounter this particular issue, and it maintains the original Props type so any existing code should be unaffected.
I found the same error when using static getDerivedStateFromProps(...)
- I was only returning state inside an if()
check - putting a return {};
outside of that fixed the error (hope this is useful to someone).
Holy sh!t. Separating out ownProps (as suggested by AndyCJ above) worked. Wish I had found this before wasting my morning.
@Rycochet This was my problem ! Thanks for the hint it didn't look it should return anything but this is it :)
The same problem goes after updating React-Redux from v6.0.3 to v6.0.7
We're using Typescript v 3.0.3
_// ComparisonView_
export interface Props {
...
}
interface State {
...
}
class ComparisonView extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
...
};
}
render() {
...
}
}
export default ComparisonView;
_// ComparisonViewContainer_
import ComparisonView, {
Props as ComparisonViewProps
} from './comparisonView
export default connect<
Partial<ComparisonViewProps>,
Partial<ComparisonViewProps>,
Partial<ComparisonViewProps>,
RootState
>(
state => {
return { ... };
},
dispatch => ({...})
);
}
})
)(ComparisonView);
Argument of type 'typeof ComparisonView' is not assignable to parameter of type 'ComponentType<Shared<Partial<Props>, Props>>'.
Type 'typeof ComparisonView' is not assignable to type 'StatelessComponent<Shared<Partial<Props>, Props>>'.
Type 'typeof ComparisonView' provides no match for the signature '(props: Shared<Partial<Props>, Props> & { children?: ReactNode; }, context?: any): ReactElement<any> | null'.
Interesting thing is that after adding some HOC to component, like react-intl, for example, all works fine.
It seems that breaking change occured at 6.0.4
type Shared<
InjectedProps,
DecorationTargetProps extends Shared<InjectedProps, DecorationTargetProps>
> = {
(6.0.3)
[P in Extract<keyof InjectedProps, keyof DecorationTargetProps>]?: DecorationTargetProps[P] extends InjectedProps[P] ? InjectedProps[P] : never;
(6.0.4)
[P in Extract<keyof InjectedProps, keyof DecorationTargetProps>]?: InjectedProps[P] extends DecorationTargetProps[P] ? DecorationTargetProps[P] : never;
};
I found the same error when using
static getDerivedStateFromProps(...)
- I was only returning state inside anif()
check - putting areturn {};
outside of that fixed the error (hope this is useful to someone).
Most helpful comment
I could only get this working by splitting everything out, its a bit verbose but there are types for everything I guess!