I'm writing a React higher-order component (HOC) with TypeScript. The HOC should accept one more prop than the wrapped component, so I wrote this:
type HocProps {
// Contains the prop my HOC needs
thingy: number
}
type Component<P> = React.ComponentClass<P> | React.StatelessComponent<P>
interface ComponentDecorator<TChildProps> {
(component: Component<TChildProps>): Component<HocProps & TChildProps>;
}
const hoc = function<TChildProps>(): (component: Component<TChildProps>) => Component<HocProps & TChildProps) {
return (Child: Component<TChildProps>) => {
class MyHOC extends React.Component<HocProps & TChildProps, void> {
// Implementation skipped for brevity
}
return MyHOC;
}
}
export default hoc;
In other words, hoc
is a function that yields the actual HOC. This HOC is (I believe) a function that accepts a Component
. Since I don't know in advance what the wrapped component will be, I'm using a generic type TChildProps
to define the shape of the props of the wrapped component. The function also returns a Component
. The returned component accepts props for the wrapped component (again, typed using the generic TChildProps
) and some props it needs for itself (type HocProps
). When using the returned component, all of the props (both HocProps
and the props for the wrapped Component
) should be supplied.
Now, when I attempt to use my HOC, I do the following:
// outside parent component
const WrappedChildComponent = hoc()(ChildComponent);
// inside parent component
render() {
return <WrappedChild
thingy={ 42 }
// Prop `foo` required by ChildComponent
foo={ 'bar' } />
}
But I get a TypeScript error:
TS2339: Property 'foo' does not exist on type 'IntrinsicAttributes & HocProps & {} & { children? ReactNode; }'
It seems to me TypeScript is not replacing TChildProps
with the shape the of the props needed for ChildComponent
. How can I make TypeScript do that?
A bit late to the party. I like to use the Omit TypeScript utility type to solve this issue. Link to the documentation: https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk
import React, {ComponentType} from 'react';
export interface AdditionalProps {
additionalProp: string;
}
export function hoc<P extends AdditionalProps>(WrappedComponent: ComponentType<P>) : ComponentType<Omit<P, 'additionalProp'>> {
const additionalProp = //...
return props => (
<WrappedComponent
additionalProp={additionalProp}
{...props as any}
/>
);
}