如何在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
相关产品推荐
相关产品推荐

