React+TypeScript:如何用映射而非条件判断收窄联合类型?
用组件映射实现带关联类型Props的React组件(无类型断言)
我需要实现一个React通用组件,它的data属性类型会随传入的type属性动态变化——data只能是Type1或Type2,且type会作为props的一部分传入。
最初的实现代码如下,但TypeScript会抛出类型错误:
interface BaseProps { className: string } interface Type1 { /* 此处定义Type1结构 */ } interface Type2 { /* 此处定义Type2结构 */ } type SomethingProps = ({type: "type1"; data: Type1} & BaseProps) | ({type: "type2"; data: Type2} & BaseProps) function Something(props: SomethingProps) { const Component = componentMap[props.type]; return <Component data={props.data} className={props.className} /> // TS错误:'Type1 | Type2'无法赋值给'Type1 & Type2' } const componentMap = { "type1": Type1Component, // FC<{data: Type1; className: string}> "type2": Type2Component // FC<{data: Type2; className: string}> }
错误原因是TypeScript无法自动推断出props.type对应的组件和props.data的类型是匹配的,它会把data当成Type1 | Type2的联合类型,而组件期望的是各自专属的类型。
用if/else分支判断可以轻松解决这个问题,但我希望仅通过组件映射的方式实现,且不使用类型断言。
解决方案
核心思路是让组件映射的类型与Props的类型强关联,让TypeScript能准确跟踪type和data的对应关系:
import React from 'react'; interface BaseProps { className: string; } // 定义具体数据类型 interface Type1 { /* Type1的结构 */ } interface Type2 { /* Type2的结构 */ } // 定义组件映射的类型,明确每个type对应的组件参数类型 type ComponentMapType = { type1: React.FC<{ data: Type1 } & BaseProps>; type2: React.FC<{ data: Type2 } & BaseProps>; }; // 实现组件映射 const componentMap: ComponentMapType = { type1: Type1Component, type2: Type2Component, }; // 基于组件映射类型自动生成Props类型,确保type和data的关联 type SomethingProps = { [K in keyof ComponentMapType]: { type: K; data: Parameters<ComponentMapType[K]>[0]['data']; } & BaseProps; }[keyof ComponentMapType]; // 使用泛型约束组件入参,让TS能准确推断类型 function Something<K extends keyof ComponentMapType>(props: Extract<SomethingProps, { type: K }>) { const Component = componentMap[props.type]; return <Component data={props.data} className={props.className} />; }
说明
- 强关联的类型定义:通过
ComponentMapType明确每个type对应的组件及其参数类型,再基于这个类型生成SomethingProps,确保type和data的类型一一对应。 - 泛型约束:组件使用泛型
K约束入参,TypeScript会根据传入的type自动推断出对应的data类型,无需额外断言。 - 无分支判断:完全通过组件映射实现渲染逻辑,保持代码简洁性。
内容的提问来源于stack exchange,提问作者brianyates
相关产品推荐
相关产品推荐

