I have a very basic stateful component where I'm using recompose to add multiple HOC to my component (in my example I only use one for simplicity). For some reason typescript is giving me an error regarding my props going into my component. How can I get rid of this error?
Here's my code:
import * as React from 'react';
import { connect } from 'react-redux';
import { compose } from 'recompose';
interface IStoreState {
readonly sessionState: {
authUser: { email: string; }
}
}
interface IAccountPageProps {
authUser: { email: string }
}
const AccountPage = ({ authUser }: IAccountPageProps ) =>
<div>
<h1>Account: {authUser.email}</h1>
</div>
const mapStateToProps = (state: IStoreState) => ({
authUser: state.sessionState.authUser,
});
export default compose(
connect(mapStateToProps)
)(AccountPage);
And the error I'm getting is:
Argument of type '({ authUser }: IAccountPageProps) => Element' is not assignable to parameter of type 'ComponentType<{}>'.
Type '({ authUser }: IAccountPageProps) => Element' is not assignable to type 'StatelessComponent<{}>'.
Types of parameters '__0' and 'props' are incompatible.
Type '{ children?: ReactNode; }' is not assignable to type 'IAccountPageProps'.
Property 'authUser' is missing in type '{ children?: ReactNode; }'.
If I don't use recompose and instead write
export default connect(mapStateToProps)(AccountPage)
I do not get any errors.
The typing for compose allows you to specify the type of the resulting component and the type of the component it can be called on, so this will avoid the errors:
Unfortunately, compose does nothing to ensure the type safety or compatibility of the functions handed to it.
So, for example, this won't generate a typing error even though it is obviously invalid:
It is safer to nest the HOC calls:
The current typing of
compose
is pretty useless. If you want to usecompose
, you have to specify theprops
type of the original and final components manually, and there is no checking that the types you specified match the list of higher-order components you passed:I'd recommend not using
compose
in TypeScript.The best solution I've found is to re-assert the type so that you get
connect
's typings too (including.WrappedComponent
which can be used for easy testing), else it defaults to anReact.ComponentClass<{}, any>
which is incorrect and gives you no information about the component as per @Matt's example above.Using your example see:
Now the returned component has the correct typing to the connect wrapper as
ConnectedComponentClass<React.StatelessComponent<IAccountPageProps>, {}>
when you go to use the component elsewhere, and now you can also access connectedComponent values such as.WrappedComponent
.ConnectedComponentClass
is react-redux's typing for it's end type of a connected component. In a perfect world this shouldn't be necessary, but it works.