React hoc Generic error - could be instantiated with a different subtype of constraint 'InjectedProps'

Viewed 90

I've stripped my example down to bare minimum to understand the problem:

Example code - Hoc.tsx

import React, { FC, ComponentType } from 'react';
import { connect } from 'react-redux';

interface ReduxProps {
  param: string;
}

interface InjectedProps {
  str: string;
}

const withWrapper = <P extends InjectedProps>(Comp: ComponentType<P>) => {
  const Wrapper: FC<ReduxProps> = ({ param }) => {
    // do sometthing with param
    // inject property
    const str = 'hello';
    return <Comp str={str} />;
  };
  Wrapper.displayName = 'Wrapper';

  // removed actual state to show example to test in typescript playground
  const mapStateToProps = () => ({
    param: 'some param'
  });

  const connector = connect(mapStateToProps);
  return connector(Wrapper);
};

export default withWrapper;

In typescript playground I get this error:

Error

Type '{ str: string; }' is not assignable to type 'P'. '{ str: string; }' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'InjectedProps'

How do inject props into my higher order component and retain the supplied generic?

1 Answers

This is a little clunky but found a solution

import React, { FC, ComponentType } from 'react';
import { connect } from 'react-redux';

interface ReduxProps {
  param: string;
}

interface InjectedProps {
  str: string;
}

const withWrapper = <P extends {}>(Comp: ComponentType<P | InjectedProps>) => {
  const Wrapper: FC<ReduxProps> = ({ param }) => {
    // do sometthing with param
    // inject property
    const str = 'hello';
    return <Comp str={str} />;
  };
  Wrapper.displayName = 'Wrapper';

  // removed actual state to show example to test in typescript playground
  const mapStateToProps = () => ({
    param: 'some param'
  });

  const connector = connect(mapStateToProps);
  return connector(Wrapper);
};

export default withWrapper;

interface Props {
  something: string;
}
const ConsumeWith: FC<Props | InjectedProps> = props => {
  const str = (props as InjectedProps).str;
  const something = (props as Props).something;

  return (
    <div>
      {str}{something}
    </div>
  );
};

const Final = withWrapper<Props>(ConsumeWith);

Related