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

如何为可接收ref的子元素定义TypeScript类型?

解决Tooltip高阶组件的children类型定义与ref处理问题

一、正确定义children的类型

要限制children为单个可接收ref的元素(原生HTML元素或forwardRef包装的组件),可以通过以下方式定义类型:

import React, { ReactElement, Ref } from 'react';

// 约束子元素必须能接收HTMLElement类型的ref
type AcceptsRefElement = ReactElement<{
  ref?: Ref<HTMLElement>;
}>;

type TooltipProps = {
  children: AcceptsRefElement;
};

如果需要更精确地限定元素类型(区分原生HTML元素和forwardRef组件),可以扩展为:

// 匹配所有原生HTML元素的props类型
type IntrinsicElementProps = React.JSX.IntrinsicElements[keyof React.JSX.IntrinsicElements];
// 匹配forwardRef组件的类型
type ForwardRefComponentProps = { ref?: Ref<HTMLElement> };

type AcceptsRefElement = ReactElement<IntrinsicElementProps | ForwardRefComponentProps>;

type TooltipProps = {
  children: AcceptsRefElement;
};

这样TypeScript会自动报错,阻止传入片段、多个子元素或无法接收ref的组件。


二、修复element.ref的类型报错问题

ReactElement的ref是作为props的一部分存在的,而非元素的直接属性。你需要从element.props.ref中获取原ref,并统一处理函数式ref和ref对象:

function Tooltip(props: TooltipProps) {
  const [targetEl, setTargetEl] = React.useState<HTMLElement | null>(null);
  
  const element = React.Children.only(props.children);
  
  // 合并自定义ref与原元素的ref
  const combinedRef = (el: HTMLElement | null) => {
    setTargetEl(el);
    const originalRef = element.props.ref;
    if (!originalRef) return;

    // 兼容函数式ref和ref对象两种形式
    if (typeof originalRef === 'function') {
      originalRef(el);
    } else {
      originalRef.current = el;
    }
  };

  const clone = React.cloneElement(element, {
    ref: combinedRef,
  });

  return clone;
}

可选优化:使用React 18+的composeRefs简化逻辑

如果你的项目使用React 18及以上版本,可以用官方提供的composeRefs工具自动合并多个ref,避免手动判断ref类型:

import { composeRefs } from 'react';

// ...

const combinedRef = composeRefs(
  (el: HTMLElement | null) => setTargetEl(el),
  element.props.ref
);

const clone = React.cloneElement(element, { ref: combinedRef });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 03:43:16