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
measure
method. - The resulting component is appropriately typed with all the original component's props as well as the two added props (
layoutParam
andonChangeLayout
).
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.