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

