如何在TypeScript中正确定义多类型对象映射函数?
TypeScript联合类型数组映射的类型兼容问题解决
问题代码
type ObjectA = { id: number; x: string; y: string; z: string; } type ObjectB = { id: number; a: string; b: string; c: string; } type Objects = (ObjectA | ObjectB)[] type MappedObject = { id: number; a: string; s: string; d: string; } type MapFn = (obj: Objects) => MappedObject[]; const mapObjectA: MapFn = (obj: ObjectA[]) => obj.map(el => ({ id: el.id, a: el.x, s: el.y, d: el.z })); const mapObjectB: MapFn = (obj: ObjectB[]) => obj.map(el => ({ id: el.id, a: el.a, s: el.b, d: el.c }));
报错信息
Type 'Objects' is not assignable to type 'ObjectA[]'.
Type 'ObjectA | ObjectB' is not assignable to type 'ObjectA'.
Type 'ObjectB' is missing the following properties from type 'ObjectA': x, y, z.
问题根源
你定义的MapFn要求参数是混合了ObjectA和ObjectB的联合数组,但mapObjectA和mapObjectB却声明只接受单一类型的数组。TypeScript的类型系统无法保证传入MapFn的数组是纯ObjectA或纯ObjectB,因此会抛出类型不兼容错误——这是联合类型的核心特性:它代表"可能是A,也可能是B"的不确定状态,而非"要么全是A,要么全是B"的二选一状态。
合规解决方案
方案1:泛型函数适配单一类型数组
如果希望保留独立的映射函数,修改MapFn为泛型类型,让每个函数只处理对应类型的数组:
type ObjectA = { id: number; x: string; y: string; z: string; } type ObjectB = { id: number; a: string; b: string; c: string; } type Objects = (ObjectA | ObjectB)[] type MappedObject = { id: number; a: string; s: string; d: string; } // 泛型MapFn,约束输入为ObjectA或ObjectB的数组 type MapFn<T extends ObjectA | ObjectB> = (obj: T[]) => MappedObject[]; const mapObjectA: MapFn<ObjectA> = (obj) => obj.map(el => ({ id: el.id, a: el.x, s: el.y, d: el.z })); const mapObjectB: MapFn<ObjectB> = (obj) => obj.map(el => ({ id: el.id, a: el.a, s: el.b, d: el.c }));
这样每个函数的输入类型明确,TypeScript会严格校验传入的数组类型,不会再报错。
方案2:判别式联合+单一处理函数(推荐)
给ObjectA和ObjectB添加判别属性,让TypeScript能自动区分不同类型,这种方式扩展性最强,新增类型时只需补充判别值和处理分支:
// 添加type字段作为判别式,明确区分类型 type ObjectA = { type: 'A'; id: number; x: string; y: string; z: string; } type ObjectB = { type: 'B'; id: number; a: string; b: string; c: string; } type Objects = (ObjectA | ObjectB)[] type MappedObject = { id: number; a: string; s: string; d: string; } const mapObjects: (obj: Objects) => MappedObject[] = (objs) => { return objs.map(obj => { switch(obj.type) { case 'A': return { id: obj.id, a: obj.x, s: obj.y, d: obj.z }; case 'B': return { id: obj.id, a: obj.a, s: obj.b, d: obj.c }; // 穷尽检查,新增类型时TypeScript会提示补充分支 default: throw new Error(`未知对象类型: ${(obj as never).type}`); } }); };
判别式联合让TypeScript在编译阶段就能确保所有类型都被处理,避免遗漏。
方案3:类型守卫函数(适合无法修改原类型的场景)
如果不能给原类型添加判别属性,可以定义类型谓词函数做类型检查,避免重复写in判断:
type ObjectA = { id: number; x: string; y: string; z: string; } type ObjectB = { id: number; a: string; b: string; c: string; } type Objects = (ObjectA | ObjectB)[] type MappedObject = { id: number; a: string; s: string; d: string; } // 类型谓词函数,判断是否为ObjectA function isObjectA(obj: ObjectA | ObjectB): obj is ObjectA { return 'x' in obj; } // 类型谓词函数,判断是否为ObjectB function isObjectB(obj: ObjectA | ObjectB): obj is ObjectB { return 'a' in obj; } const mapObjects: (obj: Objects) => MappedObject[] = (objs) => { return objs.map(obj => { if (isObjectA(obj)) { return { id: obj.id, a: obj.x, s: obj.y, d: obj.z }; } else { return { id: obj.id, a: obj.a, s: obj.b, d: obj.c }; } }); };
内容的提问来源于stack exchange,提问作者ougkr
相关产品推荐
相关产品推荐

