TypeScript中复用属性名不同的相似对象组件的类型处理
解决方案
针对组件复用、类型定义及扩展需求,这里提供几种实用方案:
方案一:组件内部做属性映射(对调用方无侵入)
无需调用方修改传入对象,组件内部根据variant字段将不同属性名统一为通用名称,避免类型错误,同时方便后续扩展。
type Obj1_Type = { id: number; packageId: number; value: string; }; type Obj2_Type = { id: number; otherId: number; value: string; }; type ComponentType = | { variant: "obj1"; obj: Obj1_Type } | { variant: "obj2"; obj: Obj2_Type }; const MyComponent = (item: ComponentType) => { // 根据variant统一获取目标属性 const targetId = item.variant === "obj1" ? item.obj.packageId : item.obj.otherId; return <> {targetId} </>; }; // 新增类型时,仅需扩展ComponentType和映射逻辑 type Obj3_Type = { id: number; customId: number; value: string }; type ComponentTypeExtended = ComponentType | { variant: "obj3"; obj: Obj3_Type }; const MyComponentExtended = (item: ComponentTypeExtended) => { let targetId: number; switch(item.variant) { case "obj1": targetId = item.obj.packageId; break; case "obj2": targetId = item.obj.otherId; break; case "obj3": targetId = item.obj.customId; break; default: throw new Error("Unknown variant"); } return <> {targetId} </>; };
方案二:定义通用类型,调用方传入标准化对象
让组件逻辑更简洁,先定义通用标准类型,要求调用方传入符合该类型的对象,或提前将原有对象转换为标准格式。
// 定义通用标准类型 type StandardObj = { id: number; targetId: number; // 统一属性名 value: string; }; // 组件直接接收标准类型,无分支逻辑 const MyComponent = (obj: StandardObj) => { return <> {obj.targetId} </>; }; // 调用方转换原有对象 const obj1: Obj1_Type = { id: 1, packageId: 100, value: "test" }; MyComponent({ ...obj1, targetId: obj1.packageId }); const obj2: Obj2_Type = { id: 2, otherId: 200, value: "demo" }; MyComponent({ ...obj2, targetId: obj2.otherId }); // 新增类型时,仅需转换为StandardObj即可,组件无需修改 type Obj3_Type = { id: number; customId: number; value: string }; const obj3: Obj3_Type = { id:3, customId:300, value:"new" }; MyComponent({ ...obj3, targetId: obj3.customId });
方案三:泛型+类型映射实现灵活扩展
通过类型映射定义不同variant对应的属性名,结合泛型让组件类型自动推导,后续新增类型仅需扩展映射表,无需修改组件逻辑。
type Obj1_Type = { id: number; packageId: number; value: string }; type Obj2_Type = { id: number; otherId: number; value: string }; type Obj3_Type = { id: number; customId: number; value: string }; // 定义variant到属性名的映射表 type VariantToProp = { obj1: "packageId"; obj2: "otherId"; obj3: "customId"; }; // 定义组件通用类型 type ComponentType<T extends keyof VariantToProp> = { variant: T; obj: { id: number; [K in VariantToProp[T]]: number; value: string; }; }; // 组件使用泛型自动推导属性名 const MyComponent = <T extends keyof VariantToProp>(item: ComponentType<T>) => { const propName = VariantToProp[item.variant]; return <> {item.obj[propName as keyof typeof item.obj]} </>; }; // 调用示例 MyComponent({ variant: "obj1", obj: { id:1, packageId:100, value:"test" } }); MyComponent({ variant: "obj2", obj: { id:2, otherId:200, value:"demo" } }); MyComponent({ variant: "obj3", obj: { id:3, customId:300, value:"new" } });
方案对比
- 方案一:适合不想改动调用方代码的场景,逻辑集中在组件内部,扩展时修改组件即可。
- 方案二:组件逻辑最简洁,转换工作交给调用方,适合需要强制标准化的场景。
- 方案三:类型约束最严格,扩展性最强,新增类型仅需更新
VariantToProp映射表,组件逻辑无需修改。
内容的提问来源于stack exchange,提问作者Gurnzbot
相关产品推荐
相关产品推荐

