How to make a React HOC type-safe yet still pass anything as an extra prop?

Viewed 40

Often times in React we have an HOC where we know there are certain required props. However, we also want the consumer of the HOC to be able to pass in any additional props they wish. This is pretty easy to do. The signature of the HOC uses destructing and rest like so:

const MyHOC = ({requiredBoolean, requiredCallback, ...restProps})

In the return, we simply pass the required props and then spread the rest:

return (
  <SomeOtherComponent
    requiredBoolean={requiredBoolean}
    requiredCallback={requiredCallback}
    {...restProps}
  />
)

The question is how to do exactly this but make it type safe with TS. Let's say that I want to make sure the required props are present and they are the correct type ...but I still want the consumer to be able to pass anything else they want.

2 Answers

I found a solution for this which seems to work, but I don't know if there is a better or more idiomatic way to do it. This is what I came up with:

interface restProps {
  [key: string]: any
}

interface myHOCProps extends restProps {
  requiredBoolean: boolean
  requiredCallback: () => void
}

It seems to work. If I remove the extends restProps I get an error in my test consumer component if I pass something random like foo='bar'. If I comment out requiredBoolean={true} in my consumer component I get an error.

So is this correct? Is there a better or more idiomatic way?

What you have doesn't seem to be a HOC, but maybe you haven't shown the full code. Instead your component simply seems to render another component and you want to make sure that props for the component itself as well as props for the "inner" component are passed.

That's pretty easy to do with interfaces. Every component describes their props as an interface and the "outer" component extends the inner component's interface.

interface InnerComponentProps {
  innerProp: boolean
}

const InnerComponent: React.FunctionComponent<InnerComponentProps> =
  ({innerProp}) => {
    // ...
  }

interface OuterComponentProps extends InnerComponentProps {
  outerProp: boolean
}


const OuterComponent: React.FunctionComponent<OuterComponentProps> =
  ({outerProp, ...innerProps}) => {
    // ...
    return <InnerComponent {...innerProps} />
  }
Related