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

如何拆分TypeScript交叉类型Props并传递给两个子组件?

拆分交叉类型Props并传递给子组件的技巧

场景说明

现有两个子组件,各自定义了复杂的Props类型:

interface Child1Props { foo: string; bar: string; baz: string }
interface Child2Props { foo: string; qux: string; quux: string }

const Child1 = (props: Child1Props) => {};
const Child2 = (props: Child2Props) => {};

父组件接收两个子组件Props的交叉类型作为自身Props,希望无需显式写出每个属性,直接用扩展运算符将对应Props传递给子组件,避免像下面这样手动拆分:

const Parent = ({foo, bar, baz, ...rest}: Child1Props & Child2Props) => (
   <Child1 foo={foo} bar={bar} baz={baz} >
     <Child2 foo={foo} {...rest} />
   </Child1>
)

目标是让父组件完全不感知子组件的具体Props,实现类似如下的简洁写法:

const Parent = (props: Child1Props & Child2Props) => (
   <Child1 {...child1Props} >
     <Child2 {...child2Props} />
   </Child1>
)

可行解决方案

方案1:工具函数+类型安全的键数组拆分

通过封装工具函数,结合预定义的子组件Props键数组(用TS语法确保键的正确性),实现自动过滤拆分:

// 通用Props过滤工具函数
const pickProps = <T extends object>(props: object, keys: (keyof T)[]): T => {
  const result = {} as T;
  keys.forEach(key => {
    if (key in props) {
      result[key] = props[key as keyof typeof props] as T[keyof T];
    }
  });
  return result;
};

// 预定义子组件的Props键名,用satisfies确保键属于对应Props类型(TS 4.9+支持)
const child1Keys = ['foo', 'bar', 'baz'] as const satisfies (keyof Child1Props)[];
const child2Keys = ['foo', 'qux', 'quux'] as const satisfies (keyof Child2Props)[];

const Parent = (props: Child1Props & Child2Props) => {
  const child1Props = pickProps<Child1Props>(props, child1Keys);
  const child2Props = pickProps<Child2Props>(props, child2Keys);
  
  return (
    <Child1 {...child1Props}>
      <Child2 {...child2Props} />
    </Child1>
  );
};

如果使用TS 4.9以下版本,可将键数组改为类型断言:const child1Keys = ['foo', 'bar', 'baz'] as (keyof Child1Props)[]

方案2:基于组件类型的通用提取函数

利用React的ComponentProps工具类型,封装通用函数提取对应组件的Props:

import type { ComponentProps } from 'react';

// 接收组件和原始Props,返回该组件所需的Props
const extractComponentProps = <C extends React.ComponentType<any>>(
  Component: C,
  props: object
): ComponentProps<C> => {
  const result = {} as ComponentProps<C>;
  // 遍历原始Props,过滤出属于目标组件的属性
  (Object.keys(props) as (keyof ComponentProps<C>)[]).forEach(key => {
    result[key] = props[key as keyof typeof props] as ComponentProps<C>[keyof ComponentProps<C>];
  });
  return result;
};

// 使用示例
const Parent = (props: Child1Props & Child2Props) => {
  const child1Props = extractComponentProps(Child1, props);
  const child2Props = extractComponentProps(Child2, props);
  
  return (
    <Child1 {...child1Props}>
      <Child2 {...child2Props} />
    </Child1>
  );
};

注意:此方案如果原始Props包含子组件不需要的额外属性,会被错误传递,建议结合键数组过滤优化。

方案3:基于TS类型推断的自动拆分

通过TS的Pick类型定义拆分后的Props类型,再通过运行时判断自动分配属性:

type ParentProps = Child1Props & Child2Props;
// 从交叉类型中提取对应子组件的Props类型
type ExtractChild1Props = Pick<ParentProps, keyof Child1Props>;
type ExtractChild2Props = Pick<ParentProps, keyof Child2Props>;

// 拆分Props的辅助函数
const splitProps = (props: ParentProps): { child1: ExtractChild1Props; child2: ExtractChild2Props } => {
  const child1 = {} as ExtractChild1Props;
  const child2 = {} as ExtractChild2Props;
  
  Object.keys(props).forEach(key => {
    const k = key as keyof ParentProps;
    // 通过空对象判断键是否属于对应子组件的Props
    if (k in ({} as Child1Props)) {
      child1[k] = props[k];
    }
    if (k in ({} as Child2Props)) {
      child2[k] = props[k];
    }
  });
  
  return { child1, child2 };
};

const Parent = (props: ParentProps) => {
  const { child1: child1Props, child2: child2Props } = splitProps(props);
  
  return (
    <Child1 {...child1Props}>
      <Child2 {...child2Props} />
    </Child1>
  );
};

注意:如果子组件包含可选属性,空对象判断会失效,此时建议结合方案1的键数组方式。

总结

如果要让父组件完全不感知子组件的具体Props,最可靠的方式是封装通用的Props提取工具函数,并配合类型安全的键数组,既保证TS类型检查,又避免手动编写属性名。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 07:47:34