DEV Community

Cover image for How to implement a Higher-order component in React with TypeScript
Max Rozen
Max Rozen

Posted on • Originally published at maxrozen.com

How to implement a Higher-order component in React with TypeScript

You might be thinking:

"Max - it's 2020! Why are you still using HoCs instead of hooks?!"

to which I'd reply

"Saying 'It's $CURRENT_YEAR...' is incredibly cliché at this point, and contributes to the JavaScript community's churn. Write boring code."

So anyway, I came across a situation in which I needed to use a React hook in a class-based component. My initial instinct was to rewrite from a class component to a function component, however upon further inspection I realised it had 1100 lines of code, with relatively complicated lifecycle methods.

I decided to wrap the component with a functional HoC that called the hook, and passed the result down to the component as a prop. In normal JavaScript, it isn't too complicated, you'd do something like this:

import React, { useState } from 'react'; export function withExtraInfo(WrappedComponent) { const [extraInfo, setExtraInfo] = useState(''); const ComponentWithExtraInfo = props => { return <WrappedComponent {...props} extraInfo={extraInfo} />; }; return ComponentWithExtraInfo; } 
Enter fullscreen mode Exit fullscreen mode

However for TypeScript, typing a HoC can become a little bit confusing, especially if you read some of the blog posts out there. If you tried to run TypeScript against the code above, you'd need to fix a few things:

  1. Both WrappedComponent and props have an implicit any type
  2. Make the function generic

Here's how we'd do that:

import React, { useState } from 'react'; // First we need to add a type to let us extend the incoming component. type ExtraInfoType = { extraInfo: string; }; // Mark the function as a generic using P (or whatever variable you want) export function withExtraInfo<P>( // Then we need to type the incoming component. // This creates a union type of whatever the component // already accepts AND our extraInfo prop WrappedComponent: React.ComponentType<P & ExtraInfoType> ) { const [extraInfo, setExtraInfo] = useState(''); setExtraInfo('important data.'); const ComponentWithExtraInfo = (props: P) => { // At this point, the props being passed in are the original props the component expects. return <WrappedComponent {...props} extraInfo={extraInfo} />;  }; return ComponentWithExtraInfo; } 
Enter fullscreen mode Exit fullscreen mode

You'll probably notice we marked withExtraInfo as a generic using <P>. For more information, see the TypeScript Handbook.

To wrap things up, you'll want to add a displayName to your HoC, which I've left as an exercise for the reader.

If this post helped you, I'd really appreciate a share on Twitter.

Top comments (1)

Collapse
 
amirjafari1992 profile image
Amir Jafari

Thank you so much, Max. It was so helpful for me :)
Just I think the useState const should be inside of ComponentWithExtraInfo