Intro
I am trying to create a Higher Order Component for a Dialog component in React Native. Sadly, I have some compile errors that I do not understand at all. I have followed this tutorial on Higher Order Components in TypeScript, but it does not show an example for getting ref to work.
Setup
I have a component called DialogLoading, and I export it through a Higher Order Component called withActions. The withActionscomponent defines two interfaces, to determine what props it injects and what additional props it takes in. In the following code, the type parameters C, A and P stand for ComponentType, ActionType and PropType respectively.
The interfaces are:
interface InjectedProps<A>
{ onActionClicked: (action: A) => void;}
and
interface ExternalProps<C, A>
{ onActionClickListener?: (component: C | null, action: A) => void;}
I also declare a type alias that denotes the final props type for the HOC. This type should have all the props of the wrapped component, all the props of the ExternalProps<C, A> interface, but not the props of the InjectedProps<A> interface. This is declared as follows:
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
type Subtract<T, K> = Omit<T, keyof K>;
type HocProps<C, A, P extends InjectedProps<A>> = Subtract<P, InjectedProps<A>> & ExternalProps<C, A>;
The Higher Order Component is then declared as follows:
export default <C, A, P extends InjectedProps<A>> (WrappedComponent: React.ComponentType<P>) =>
{
const hoc = class WithActions extends React.Component<HocProps<C, A, P>>
{
...Contents of class removed for breivity.
private onActionClicked = (action: A) =>
{
this.onActionClickedListeners.forEach(listener =>
{ listener(this.wrapped, action);});
}
private wrapped: C | null;
render()
{
return (
<WrappedComponent ref={i => this.wrapped = i} onActionClicked={this.onActionClicked} {...this.props} />
);
}
}
return hoc;
}
and can be used by:
<DialogLoading onActionClickListener={this.onActionClickListener} title="Loading Data" section="Connecting" />;
The Problem
On the ref callback inside the render function of the HOC, TypeScript gives me the following error messages:
[ts] Property 'ref' does not exist on type 'IntrinsicAttributes & InjectedProps<A> & { children?: ReactNode; }'.
[ts] Type 'Component<P, ComponentState, never> | null' is not assignable to type 'C | null'.
Type 'Component<P, ComponentState, never>' is not assignable to type 'C'.
I suspect this is because the WrappedComponent that is passed in, is of type React.ComponentType<P>, which is a union type of React.ComponentClass<P> and React.SFC<P>. The error is thrown, because the stateless components in React do not accept a ref callback. A possible solution would then be to change it's type to be just React.ComponentClass<P>.
This kind of fixes the problem, but strangely enough, a new error is now thrown on the onActionClicked prop of the wrapped component! The error is:
[ts] Type '(action: A) => void' is not assignable to type '(IntrinsicAttributes & IntrinsicClassAttributes<Component<P, ComponentState, never>> & Readonly<{ children?: ReactNode; }> & Readonly<P>)["onActionClicked"]'.
WithActions.tsx(7, 5): The expected type comes from property 'onActionClicked' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<P, ComponentState, never>> & Readonly<{ children?: ReactNode; }> & Readonly<P>'
This second error baffles me completely. What makes it even stranger is that when I adjust the type alias for the HocProps to the following (i.e. I no longer Subtract InjectedProps<A> from P):
type HocProps<C, A, P extends InjectedProps<A>> = P & ExternalProps<C, A>;
The error on onActionClicked is removed! This seems strange to me, because the type definition of HocProps has nothing to do with the prop type of the wrapped component! This "solution", however, is undesirable to me, because now the InjectedProps<A> are injectable by the user of the HOC as well.
The Question
So where am I going wrong here?
Am I correct in assuming the ref callback did not work because the
Wrapped Componenttype wasReact.ComponentType<P>instead ofReact.ComponentClass<P>?Why does changing the
Wrapped Componenttype toReact.ComponentClass<P>result in a compile error on theonActionClickedprop of theWrapped Component?Why does changing the type alias for
HocPropsremove said error on theonActionClickedprop? Aren't they completely unrelated?Is the
Subtractfunctionality I've cooked up correct? Is that where the errors are coming from?
Any help would be greatly appreciated, so thanks in advance!