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

如何在TypeScript中推断并约束React首个子组件的Props?

在TypeScript中推断首个子组件的Props并实现类型约束

我需要实现一个包装组件,向首个子组件传递Props时保证类型安全:传递不存在的Props时TypeScript报错,合法Props则正常通过。目前已接近实现,但泛型场景下无法正确推断类型,现有代码如下:

import React, {
  Children,
  isValidElement,
  cloneElement,
  ReactNode,
} from 'react';

interface WrapperProps<C> {
  children: ReactNode;
  // 如何让typeof Button从代码中自动推断?
  // firstChildProps: Partial<React.ComponentProps<typeof Button>>; // 去掉<C>时可以正常工作
  firstChildProps: Partial<React.ComponentPropsWithoutRef<C>>;
}

const Wrapper: React.FC<WrapperProps<typeof Button>> = ({
  children,
  firstChildProps,
}) => {
  const firstChild = Children.toArray(children)[0];
  if (isValidElement(firstChild)) {
    // 克隆首个子元素并传递firstChildProps
    return cloneElement(firstChild, { ...firstChildProps });
  } else {
    return <>{children}</>;
  }
};

interface ButtonProps {
  disabled: boolean;
  children: ReactNode;
}

const Button: React.FC<ButtonProps> = ({ disabled, children }) => {
  return <button disabled={disabled}>{children}</button>;
};

const Example = () => {
  return (
    <>
      {/* 类型检查通过,因为disabled是Button的合法Props */}
      <Wrapper firstChildProps={{ disabled: false }}>
        <Button disabled={true}>Ok</Button>
      </Wrapper>
      {/* 类型检查失败,因为cheese不是Button的合法Props */}
      <Wrapper firstChildProps={{ cheese: true }}>
        <Button disabled={true}>Ok</Button>
      </Wrapper>
    </>
  );
};

解决方案

要实现自动推断首个子组件的类型并约束Props,需要将Wrapper改造为泛型组件,通过类型提取从children中获取首个有效元素的类型:

import React, {
  Children,
  isValidElement,
  cloneElement,
  ReactElement,
} from 'react';

// 泛型Props,C代表首个子组件的类型
interface WrapperProps<C extends React.ElementType> {
  // 约束children的首个元素为C类型的ReactElement
  children: ReactElement<C> | [ReactElement<C>, ...React.ReactNode[]];
  // 自动推断C的Props类型,生成Partial约束
  firstChildProps: Partial<React.ComponentPropsWithoutRef<C>>;
}

// 定义泛型组件,不指定具体C,让TS自动推断
const Wrapper = <C extends React.ElementType>({
  children,
  firstChildProps,
}: WrapperProps<C>) => {
  const firstChild = Array.isArray(children) ? children[0] : children;
  if (isValidElement(firstChild)) {
    return cloneElement(firstChild, { ...firstChildProps });
  } else {
    return <>{children}</>;
  }
};

interface ButtonProps {
  disabled: boolean;
  children: React.ReactNode;
}

const Button: React.FC<ButtonProps> = ({ disabled, children }) => {
  return <button disabled={disabled}>{children}</button>;
};

const Example = () => {
  return (
    <>
      {/* 类型检查通过 */}
      <Wrapper firstChildProps={{ disabled: false }}>
        <Button disabled={true}>Ok</Button>
      </Wrapper>
      {/* TS会报错:类型"{ cheese: true; }"中缺少属性"disabled",且"cheese"不在类型"Partial<ButtonProps>"中 */}
      <Wrapper firstChildProps={{ cheese: true }}>
        <Button disabled={true}>Ok</Button>
      </Wrapper>
    </>
  );
};

关键说明

  • 通过C extends React.ElementType约束泛型参数为合法的React组件类型
  • 将children的类型约束为ReactElement<C> | [ReactElement<C>, ...React.ReactNode[]],确保首个子元素是目标组件类型,让TypeScript能自动推断C
  • 使用React.ComponentPropsWithoutRef<C>获取子组件的Props类型,保证firstChildProps的类型安全
  • 组件定义时不固定C的具体值,完全由传入的children自动推断

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 20:56:04