1

Very much looking for some help on this

Libs used: recompose, react-redux

The issue: When composing redux's connect with other HOC's with compose I'm losing types within compose and trying to access from a text context ConnectedComponent.WrappedComponent throws a type error: 'Property 'WrappedComponent' does not exist on type 'ComponentClass<{}, any>'.

The Code

import React, { Fragment, SFC } from 'react';
import { connect } from 'react-redux';
import { branch, renderComponent, compose } from 'recompose';
import { RootState } from '../../types/state';

interface StateProps {
  foo?: boolean;
}

const Component: SFC<StateProps> = ({ children }) => <Fragment>{children}</Fragment>;

const mapStateToProps = (state: RootState): StateProps => ({
  foo: state.foo
});

export default compose<StateProps, {}>(
  connect(mapStateToProps),
  branch<StateProps>(
    ({ foo }) => !initialised,
    renderComponent(() => <div className="branched" />)
  )
)(Component);

Then Accessing the wrapped component errors as the types are not carried through:

const Gateway = ConnectedGateway.WrappedComponent;
                                 ^^^^^^^^^^^^^^^^^ ->'Property 'WrappedComponent' does not exist on type 'ComponentClass<{}, any>'

There are a few solutions I've found that seem to work, changing the composition for example

connect(mapStateToProps)(compose(
  // Stuff Here
)(Component))

Keeps the types, but shouldn't be necessary as from a FP standpoint it's exactly the same as compose(connect, branch)(Component)

The other is extending ComponentClass and exporting as unknown as IComponent which feels like a lot of hoops to jump through:

import React, { Fragment, SFC, ComponentClass } from 'react';
import { connect } from 'react-redux';
import { branch, renderComponent, compose } from 'recompose';
import { RootState } from '../../types/state';

interface StateProps {
  foo?: boolean;
}

interface IComponent extends ComponentClass<StateProps, {}> {
  WrappedComponent: SFC<StateProps>;
}

const Component: SFC<StateProps> = ({ children }) => <Fragment>{children}</Fragment>;

const mapStateToProps = (state: RootState): StateProps => ({
  foo: state.foo
});

export default (compose<StateProps, {}>(
  connect(mapStateToProps),
  branch<StateProps>(({ foo }) => !foo, renderComponent(() => <div className="branched" />))
)(Component) as unknown) as IComponent;

Any thoughts would be appreciated.

EDIT: BETTER SOLUTION

You can cast it to the correct type using ConnectedComponentClass which simplifies the typing a lot.

import React, { Fragment, SFC, ComponentClass } from 'react';
import { connect, ConnectedComponentClass } from 'react-redux';
import { branch, renderComponent, compose } from 'recompose';
import { RootState } from '../../types/state';

interface StateProps {
  foo?: boolean;
}

const Component: SFC<StateProps> = ({ children }) => <Fragment>{children}</Fragment>;

const mapStateToProps = (state: RootState): StateProps => ({
  foo: state.foo
});

export default compose<StateProps, {}>(
  connect(mapStateToProps),
  branch<StateProps>(({ foo }) => !foo, renderComponent(() => <div className="branched" />))
)(Component) as ConnectedComponentClass<SFC<StateProps>, {}>;
Peak
  • 349
  • 2
  • 6

0 Answers0