You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript创建严格类型React HOC包装RN组件的类型报错问题

问题背景

尝试创建高阶组件(HOC)包装React Native组件,基于组件ref添加布局测量功能。未加类型的JS实现示例如下:

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是React Native元素从NativeMethods接口继承的方法,用于获取元素渲染后的位置、尺寸信息。

需求目标

需要实现两点严格类型约束:

  • 该HOC仅可用于ref实例带有measure方法的元素
  • HOC返回的组件需正确透传原组件所有props,同时新增layoutParam、onChangeLayout两个必填props
问题复现

当前编写的TS实现代码如下:

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} />;
  };
}

编译时报错如下:

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>>'.
问题根因

报错由三个问题共同导致:

  1. 笔误:WithHelperProps接口中onChangeLayout的参数属性写为payoutParam,和实际代码中传入的layoutParam不一致
  2. 泛型约束范围过窄:仅约束了类组件类型,没有覆盖React Native内置原生组件(View/Text等属于HostComponent,不是普通ComponentClass)、forwardRef包装的组件
  3. 冗余类型标注错误:手动给passthroughProps做Omit类型标注时,没有兼容React对组件props做的defaultProps处理、属性过滤等LibraryManagedAttributes逻辑,手动标注的类型和React实际期望的props类型不兼容
修复后实现
import React, { useRef, useCallback, ComponentRef } from 'react';
import type { LayoutRectangle, NativeMethods, LayoutChangeEvent } from 'react-native';

interface WithHelperExtraProps {
  layoutParam: string;
  onChangeLayout: (data: { layout: LayoutRectangle; layoutParam: string }) => void;
}

export function withHelper<
  T extends React.ComponentType<{ onLayout?: (e: LayoutChangeEvent) => void }>
>(WrappedComponent: T) {
  // 静态类型校验:传入组件的ref必须包含NativeMethods的measure方法,不满足则编译报错
  type WrappedInstance = ComponentRef<T>;
  const _typeCheck: NativeMethods extends WrappedInstance ? unknown : never = 0 as unknown;

  type ResultProps = Omit<React.ComponentProps<T>, keyof WithHelperExtraProps> & WithHelperExtraProps;
  const WrappedWithHelper = ({ layoutParam, onChangeLayout, ...restProps }: ResultProps) => {
    const ref = useRef<WrappedInstance & NativeMethods>(null);

    const onLayout = useCallback(() => {
      ref.current?.measure((_x, _y, width, height, pageX, pageY) => {
        onChangeLayout({
          layout: {
            width,
            height,
            x: pageX,
            y: pageY,
          },
          layoutParam,
        });
      });
    }, [onChangeLayout, layoutParam]);

    return <WrappedComponent {...restProps} ref={ref} onLayout={onLayout} />;
  };

  return WrappedWithHelper;
}

实现说明

  • 泛型直接约束传入组件为支持onLayout属性的组件类型,覆盖类组件、函数组件、forwardRef组件、RN原生宿主组件所有场景
  • 通过ComponentRef<T>提取传入组件的ref实例类型,加静态类型校验确保实例继承NativeMethods接口,满足「仅可用于带measure方法元素」的约束
  • 去掉冗余的passthroughProps手动类型标注,直接透传剩余props,由TS自动推导类型匹配React的props处理逻辑,避免LibraryManagedAttributes类型不兼容问题
  • 修正了原接口中payoutParam的笔误,统一为layoutParam
  • 返回值的props类型通过Omit<原组件Props, HOC注入Props> & HOC新增Props计算,确保既透传原组件所有属性,又强制要求传入新增的两个props

内容的提问来源于stack exchange,提问作者Martijn Arts

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 04:45:38