是否可以在 Typescript 中创建严格类型的 React HOC?

Mar*_*rts 7 typescript reactjs react-native

作为上下文,我尝试创建一个更高阶的组件来包装 React Native 组件,并根据组件的引用添加一些功能。

简短的无类型示例:

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} />;
  };
}
Run Code Online (Sandbox Code Playgroud)

measure是一个 React Native 元素,它为我提供了一些有关渲染其他内容的元素所需的信息。是从界面来的NativeMethods

我想在这里严格输入的内容:

  • HOC 只能用于具有该measure方法的元素。
  • 生成的组件使用所有原始组件的 props 以及两个添加的 props (layoutParamonChangeLayout) 进行了适当的类型化。

这就是我走了多远:

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} />;
  };
}
Run Code Online (Sandbox Code Playgroud)

但这不能编译:

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>>'.
Run Code Online (Sandbox Code Playgroud)

我已经玩了一段时间并四处搜索,但我不明白我在这里做错了什么。


在 Replit 中做了一个最小的复制

Moa*_*Moa 4

我认为您只是忘记了forwardRef组件的ref,这是需要的,因为 HOC 将包装另一个组件。为了演示,这是一个简化版本(我将立即更新到您的具体用例)。

interface Layout {
  height: number
  width: number
  x: number
  y: number
}

interface WithHelperProps {
  children?: React.ReactNode
  layoutParam: string
  onChangeLayout: (props: { layout: Layout, layoutParam: string }) => void
}

const withHelper = <T, P = {}>(
  Render: React.ComponentType<P & { ref?: React.Ref<T> }>,
) => {
  const HOC = React.forwardRef<T, P & WithHelperProps>(
    ({ children, ...props }, ref) => {
      return (
        <Render {...(props as P)} ref={ref}>
          {children}
        </Render>
      )
    },
  )
  return HOC
}
Run Code Online (Sandbox Code Playgroud)

希望有帮助!干杯