Create a TypeScript HOC (Higher Order Component) in React

You may have a number of custom HOC’s (higher order components) in your components deck, and now you’ve implemented TypeScript to your stack.


function youreAlwaysCoolHOC(ChildComp) {
  return class Component extends React.Component {
    state = {
      areYouCool: false
    };

    handleClick = () => this.setState({ areYouCool: true });

    render() {
      return (
        <>
          <button onClick={this.handleClick}>Find yout if you're cool</button>
          <ChildComp areYouCool={this.state.areYouCool} />
        </>
      );
    }
  };
}

All of sudden you see errors about your child component parameter is type any implicitly.


Parameter 'ChildComp' implicitly has an 'any' type

Here’s how you define your component parameter type.

Answer: TypeScript interface React.ComponentType

I’ve created a new a HOC (higher order component) called youreAlwaysCoolHOC().


interface AwaysCoolStateProps {
  areYouCool: boolean;
}

function youreAlwaysCoolHOC(ChildComp: React.ComponentType<any | string>) {
  return class Component extends React.Component<{}, AwaysCoolStateProps> {
    state = {
      areYouCool: false
    };

    handleClick = () => this.setState({ areYouCool: true });

    render() {
      return (
        <>
          <button onClick={this.handleClick}>Find yout if you're cool</button>
          <ChildComp areYouCool={this.state.areYouCool} />
        </>
      );
    }
  };
}

youreAlwaysCoolHOC() will accept a parameter that should be a React component.


function youreAlwaysCoolHOC(ChildComp: React.ComponentType<any | string>) {}

I’ve also defined a variable ChildComp as React.ComponentType. This type interface will accept class components and functional components.

Let’s take a closer look at what youreAlwaysCoolHOC() does.


interface AwaysCoolStateProps {
  areYouCool: boolean;
}

class Component extends React.Component<{}, AwaysCoolStateProps> {
    state = {
      areYouCool: false
    };

    handleClick = () => this.setState({ areYouCool: true });

    render() {
      return (
        <>
          <button onClick={this.handleClick}>Find yout if you're cool</button>
          <ChildComp areYouCool={this.state.areYouCool} />
        </>
      );
    }
  };

youreAlwaysCoolHOC() returns a new, and enhanced React component that displays a button for a user to click on.

It also passes the state value as a prop to the child component.

Here’s how to use the React HOC.


interface ContainerProps {
  areYouCool: boolean;
}

const Container: React.SFC<ContainerProps> = props => (
  <h1>{props.areYouCool ? "Heck yes I'm cool! :)" : ":("}</h1>
);

const App = youreAlwaysCoolHOC(Container);

To accept the React prop that youreAlwaysCoolHOC() provides, I must extend the React component interface. That’s where ContainerProps comes into play.

And that’s it!

Conclusion

Use the type interface React.ComponentType to define the component parameter in your custom React HOC (higher order component).

Full code example


interface ContainerProps {
  areYouCool: boolean;
}

interface AwaysCoolStateProps {
  areYouCool: boolean;
}

function youreAlwaysCoolHOC(ChildComp: React.ComponentType<any | string>) {
  return class Component extends React.Component<{}, AwaysCoolStateProps> {
    state = {
      areYouCool: false
    };

    handleClick = () => this.setState({ areYouCool: true });

    render() {
      return (
        <>
          <button onClick={this.handleClick}>Find yout if you're cool</button>
          <ChildComp areYouCool={this.state.areYouCool} />
        </>
      );
    }
  };
}

const Container: React.SFC<ContainerProps> = props => (
  <h1>{props.areYouCool ? "Heck yes I'm cool! :)" : ":("}</h1>
);

const App = youreAlwaysCoolHOC(Container);

I like to tweet about React and post helpful code snippets. Follow me there if you would like some too!