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

React Native TypeScript中组件ref类型定义报错问题

React Native + TypeScript 原生组件ref类型定义报错问题

问题描述

在React Native项目中使用TypeScript开发时,ref及其类型定义环节出现类型报错,需要确认正确写法。

开发目标为编写原生组件包装类MyComponentView,在类内部获取指向原生组件MyComponentViewNative实例的ref引用viewRef,并调用该原生组件的start方法。这套ref逻辑在纯JavaScript环境下可正常运行,但迁移到TypeScript环境后始终无法通过类型检查。

相关实现代码如下:

type MyComponentProps = {
  ref?: React.RefObject<typeof MyComponentViewNative>;
  style?: ViewStyle;
};

type MyComponentState = {
  permissionsGranted?: Boolean;
};

const ComponentName = 'MyComponentView';

const MyComponentViewNative =
  UIManager.getViewManagerConfig(ComponentName) != null
    ? requireNativeComponent<MyComponentProps>(ComponentName)
    : () => {
        throw new Error(LINKING_ERROR);
      };

export class MyComponentView extends React.PureComponent<{}, {}> {
  private viewRef: React.RefObject<typeof MyComponentViewNative> =
    React.createRef<typeof MyComponentViewNative>();

  public state: MyComponentState = {
    permissionsGranted: false,
  };

  async componentDidMount() {
    // 业务逻辑省略
    this.setState({ permissionsGranted: true });
  }

  start(filePath: String) {
    callViewMethod(this.viewRef, 'start', filePath);
  }

  render() {
    const { permissionsGranted } = this.state;

    return permissionsGranted ? (
      <MyComponentViewNative {...this.props} ref={this.viewRef} />
    ) : (
      <View {...this.props} />
    );
  }
}

function callViewMethod(
  viewRef: React.RefObject<typeof MyComponentViewNative>,
  methodName: string,
  ...params: any[]
) {
  if (viewRef.current == null) {
    return;
  }

  if (Platform.OS === 'ios') {
    NativeModules.MyComponentViewNative[methodName].apply(
      NativeModules.MyComponentViewNative,
      [findNodeHandle(viewRef.current), ...params]
    );
  } else if (Platform.OS === 'android') {
    UIManager.dispatchViewManagerCommand(
      findNodeHandle(viewRef.current),
      methodName,
      params
    );
  }
}

tsc编译器抛出的典型错误信息如下:

src/index.tsx:124:23 - error TS2345: Argument of type 'HostComponent<MyComponentProps> | (() => never)' is not assignable to parameter of type 'number | ComponentClass<any, any> | Component<any, any, any> | null'.
  Type '() => never' is not assignable to type 'number | ComponentClass<any, any> | Component<any, any, any> | null'.

124       [findNodeHandle(viewRef.current), ...params]

待确认问题:

  • 当前写法存在什么问题?
  • viewRef属性应该使用什么类型进行定义?
  • 是否存在可以跳过显式类型设置的解决方案?

问题原因与修复方案

核心报错原因

报错根源是MyComponentViewNative被推导为联合类型HostComponent<MyComponentProps> | (() => never):三元表达式的两个分支分别返回原生组件、抛出错误的兜底函数,TS静态检查时无法确定运行时viewRef.current一定是符合findNodeHandle参数要求的原生组件实例,兜底函数返回never类型,和findNodeHandle的参数类型不匹配,因此抛出错误。

另外原代码中ref的类型定义也存在错误:typeof MyComponentViewNative拿到的是组件构造函数/组件本身的类型,不是ref挂载后拿到的组件实例类型。

正确写法

  1. 拆分原生组件加载和链接校验逻辑,避免联合类型污染组件类型:
    const ComponentName = 'MyComponentView';
    // 直接加载原生组件,拿到准确的HostComponent类型
    const MyComponentViewNative = requireNativeComponent<MyComponentProps>(ComponentName);
    // 链接校验单独处理,不混入组件赋值逻辑
    if (UIManager.getViewManagerConfig(ComponentName) == null) {
      throw new Error(LINKING_ERROR);
    }
    
  2. 使用React.ElementRef工具类型获取组件的实例类型,作为ref的泛型参数:
    // 定义原生组件实例的正确类型
    type MyComponentNativeInstance = React.ElementRef<typeof MyComponentViewNative>;
    
    export class MyComponentView extends React.PureComponent<{}, {}> {
      // 用实例类型声明ref
      private viewRef = React.createRef<MyComponentNativeInstance>();
    
      // 其余state、生命周期、render逻辑保持不变
    }
    
    // 同步修改工具函数的参数类型
    function callViewMethod(
      viewRef: React.RefObject<MyComponentNativeInstance>,
      methodName: string,
      ...params: any[]
    ) {
      const nodeRef = viewRef.current;
      if (!nodeRef) return;
      const nodeHandle = findNodeHandle(nodeRef);
      if (!nodeHandle) return;
    
      // 后续原生方法调用逻辑保持不变
      if (Platform.OS === 'ios') {
        NativeModules.MyComponentViewNative[methodName].apply(
          NativeModules.MyComponentViewNative,
          [nodeHandle, ...params]
        );
      } else if (Platform.OS === 'android') {
        UIManager.dispatchViewManagerCommand(
          nodeHandle,
          methodName,
          params
        );
      }
    }
    

跳过显式类型检查的方案

如果不需要类型校验,可以直接用any类型绕开TS检查,这种写法不会出现类型报错,但会完全丢失类型安全,不推荐在生产环境使用:

// 类组件中直接声明为any,不需要写泛型参数
private viewRef: any = React.createRef();

内容的提问来源于stack exchange,提问作者Vyacheslav Orlovsky

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:33:23