Search code examples
javascriptreactjsflowtyperecompose

Type HOC that enhances component with its own properties?


I'm trying to type an Higher Order Component (HOC) using Recompose and its HOC type, using Flow.

This is my code:

// @flow
import React from 'react';
import { compose, defaultProps, withProps, type HOC } from 'recompose';

type InnerProps = {
  text: string,
  num: number,
};

type EnhancedComponentProps = {
  text: string,
};

const baseComponent = ({ text, num }: InnerProps) => (
  <div>
    {text}
    {num}
  </div>
);

const enhance: HOC<*, EnhancedComponentProps> = compose(
  defaultProps({
    text: 'world',
  }),
  withProps(({ text }) => ({
    text: `Hello ${text}`,
  }))
);

export default enhance(baseComponent);

Right now this fails with:

Cannot call enhance with baseComponent bound to a because property num is missing in object type [1] but exists in
InnerProps [2] in the first argument.

     src/Text.js
 [2] 14│ const baseComponent = ({ text, num }: InnerProps) => (
       :
     27│   }))
     28│ );
     29│
     30│ export default enhance(baseComponent);
     31│

     flow-typed/npm/recompose_v0.x.x.js
 [1] 95│   ): HOC<{ ...$Exact<Enhanced>, ...BaseAdd }, Enhanced>;

Trying to read the docs and some blog posts I couldn't reach to a solution. All the examples I find are very trivial and none of them cover this simple case.

What's the right way to type this code?


Solution

  • I guess you get the right error. It says:

    num is missing in object type [1] but exists in InnerProps [2] in the first argument.

    You declared that your HOC will get what's in EnhancedComponentProps which is missing the num. In other words, you try to extract num from Object that will only get what's declared in EnhancedComponentProps type.

    Based on recompose docs: you should get this work by:

    // @flow
    import React from 'react';
    import { compose, defaultProps, withProps, type HOC } from 'recompose';
    
    type EnhancedComponentProps = {
      text: string,
      num: number,
    };
    
    const baseComponent = ({ text, num }: EnhancedComponentProps) => ( // in the example from recompose this typing is unnecessary though
      <div>
        {text}
        {num}
      </div>
    );
    
    const enhance: HOC<*, EnhancedComponentProps> = compose(
      defaultProps({
        text: 'world',
      }),
      withProps(({ text }) => ({
        text: `Hello ${text}`,
      }))
    );
    
    export default enhance(baseComponent);