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

如何实现TypeScript+React条件组件及属性的类型安全?

问题:根据字符串渲染组件的TypeScript类型安全实现

我需要根据字符串渲染对应组件,目前实现了如下方案:

const MY_COMPONENTS = {
  COMPONENT_ONE: "ComponentOne",
  COMPONENT_TWO: "ComponentTwo"
};

const MY_COMPONENTS_MAP = {
  [MY_COMPONENTS.COMPONENT_ONE]: ComponentOne,
  [MY_COMPONENTS.COMPONENT_TWO]: ComponentTwo
};

由于两个组件的属性类型结构不同,尽管条件渲染功能正常,但TypeScript仍会报错,相关代码如下:

// 改成'ComponentOne'或'ComponentTwo'可以渲染不同组件
const COMPONENT_TO_RENDER = "ComponentOne";

export default function App() {
  const ConditionalComponent = MY_COMPONENTS_MAP[COMPONENT_TO_RENDER];

  const getConditionalComponentProps = (component: string) => {
    switch (component) {
      case MY_COMPONENTS.COMPONENT_ONE:
        return {
          id: "123",
          name: "Jhon Doe"
        };
      case MY_COMPONENTS.COMPONENT_TWO:
        return {
          logLoremIpsum: () => console.log("Lorem Ipsum!")
        };
    }
  };

  return (
    <div className="App">
      {/* 如何修复下面的类型错误? */}
      <ConditionalComponent
        {...getConditionalComponentProps(COMPONENT_TO_RENDER)}
      />
    </div>
  );
}

TypeScript错误信息:

Type '{} | { id: string; name: string; logLoremIpsum?: undefined; } | { logLoremIpsum: () => void; id?: undefined; name?: undefined; }' is not assignable to type 'IntrinsicAttributes & ComponentOneProps & ComponentTwoProps'.
  Type '{}' is missing the following properties from type 'ComponentOneProps': id, namets(2322)

请问如何让ConditionalComponent在TypeScript中实现类型安全?或者是否有其他条件组件渲染的可行方案?


解决方案

方案一:用联合类型绑定组件与Props的类型关系

首先明确组件的Props类型,再通过TypeScript的联合类型和工具类型,让组件标识、组件类型、Props类型形成强绑定:

// 先定义组件的Props类型
type ComponentOneProps = {
  id: string;
  name: string;
};

type ComponentTwoProps = {
  logLoremIpsum: () => void;
};

// 定义组件标识的类型
type ComponentKey = keyof typeof MY_COMPONENTS;

// 工具类型:根据组件标识获取对应Props类型
type ComponentProps<K extends ComponentKey> = 
  K extends typeof MY_COMPONENTS.COMPONENT_ONE ? ComponentOneProps :
  K extends typeof MY_COMPONENTS.COMPONENT_TWO ? ComponentTwoProps :
  never;

// 给组件映射对象添加精确类型约束
const MY_COMPONENTS_MAP: {
  [K in ComponentKey]: React.ComponentType<ComponentProps<K>>
} = {
  [MY_COMPONENTS.COMPONENT_ONE]: ComponentOne,
  [MY_COMPONENTS.COMPONENT_TWO]: ComponentTwo
};

接着修改Props生成函数,让它返回精确的类型:

const getConditionalComponentProps = <K extends ComponentKey>(component: K): ComponentProps<K> => {
  switch (component) {
    case MY_COMPONENTS.COMPONENT_ONE:
      return { id: "123", name: "Jhon Doe" } as ComponentProps<K>;
    case MY_COMPONENTS.COMPONENT_TWO:
      return { logLoremIpsum: () => console.log("Lorem Ipsum!") } as ComponentProps<K>;
    default:
      throw new Error(`不支持的组件类型:${component}`);
  }
};

最后在App组件中,给COMPONENT_TO_RENDER指定ComponentKey类型,让TypeScript自动推断对应关系:

const COMPONENT_TO_RENDER: ComponentKey = "ComponentOne";

export default function App() {
  const ConditionalComponent = MY_COMPONENTS_MAP[COMPONENT_TO_RENDER];
  const props = getConditionalComponentProps(COMPONENT_TO_RENDER);

  return (
    <div className="App">
      <ConditionalComponent {...props} />
    </div>
  );
}

方案二:用泛型组件统一处理渲染

创建一个泛型的条件渲染组件,把组件标识、组件、Props的对应关系集中处理,TypeScript会自动校验类型匹配:

// 定义组件配置的联合类型
type ComponentConfig = 
  | { key: typeof MY_COMPONENTS.COMPONENT_ONE; props: ComponentOneProps }
  | { key: typeof MY_COMPONENTS.COMPONENT_TWO; props: ComponentTwoProps };

const ConditionalRenderer = ({ key, props }: ComponentConfig) => {
  switch (key) {
    case MY_COMPONENTS.COMPONENT_ONE:
      return <ComponentOne {...props} />;
    case MY_COMPONENTS.COMPONENT_TWO:
      return <ComponentTwo {...props} />;
    default:
      return null;
  }
};

// 使用示例
export default function App() {
  return (
    <div className="App">
      <ConditionalRenderer 
        key={MY_COMPONENTS.COMPONENT_ONE}
        props={{ id: "123", name: "Jhon Doe" }}
      />
    </div>
  );
}

方案三:直接用条件判断渲染(简单场景首选)

如果组件数量不多,直接用switch或if-else渲染是最直观且无类型复杂度的方式:

export default function App() {
  const componentKey = MY_COMPONENTS.COMPONENT_ONE;

  switch (componentKey) {
    case MY_COMPONENTS.COMPONENT_ONE:
      return <ComponentOne id="123" name="Jhon Doe" />;
    case MY_COMPONENTS.COMPONENT_TWO:
      return <ComponentTwo logLoremIpsum={() => console.log("Lorem Ipsum!")} />;
    default:
      return null;
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 11:40:41