As context, I'm attempting to create a higher order component to wrap React Native components and add some functionality based on a ref to the component.
Short, untyped example:
export function withHelper(WrappedEl) {
return ({
layoutParam,
onChangeLayout,
...wrappedElProps
}) => {
const ref = useRef(null);
const onLayout = useCallback(() => {
ref.current?.measure((_x, _y, width, height, px, py) => {
onChangeLayout({
layout: {
width,
height,
y: py,
x: px,
},
layoutParam,
});
});
}, [
onChangeLayout,
layoutParam,
]);
return <WrappedEl {...wrappedElProps} ref={ref} onLayout={onLayout} />;
};
}
measure is a React Native element that gives me some information I need about the element for rendering other things. It's from the interface NativeMethods.
What I'd like to get strictly typed here:
- The HOC can only be used on elements that have the
measuremethod. - The resulting component is appropriately typed with all the original component's props as well as the two added props (
layoutParamandonChangeLayout).
This is how far I got:
interface WithHelperProps {
layoutParam: string;
onChangeLayout: (props: {layout: Layout, payoutParam: string}) => void;
}
export function withHelper<
P extends object,
V extends React.ComponentClass<P> & Constructor<NativeMethods>,
>(El: V): React.FC<P & WithHelperProps> {
return ({
layoutParam,
onChangeLayout,
...props
}) => {
const ref = useRef<InstanceType<V>>(null);
const onLayout = useCallback(() => {
ref.current?.measure((_x, _y, width, height, px, py) => {
onChangeLayout({
layout: {
width,
height,
y: py,
x: px,
},
layoutParam,
});
});
}, [
onChangeLayout,
layoutParam,
]);
const passthroughProps: Omit<
PropsWithChildren<P & WithHelperProps>,
keyof WithHelperProps
> = props;
return <El {...passthroughProps} ref={ref} onLayout={onLayout} />;
};
}
But this doesn't compile:
Type 'Omit<PropsWithChildren<P & WithHelperProps>, keyof WithHelperProps> & { ref: RefObject<InstanceType<V>>; onLayout: () => void; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<P, any, any> & NativeMethods> & LibraryManagedAttributes<...>'.
Type 'Omit<PropsWithChildren<P & WithHelperProps>, keyof WithHelperProps> & { ref: RefObject<InstanceType<V>>; onLayout: () => void; }' is not assignable to type 'LibraryManagedAttributes<V, Readonly<P>>'.
I've been playing with it for a while and searching around, but I can't figure out what I'm doing wrong here.
I made a minimal reproduction in Replit.