TypeScript可辨识联合未收窄致函数映射类型错误排查
解决方案
方案1:修改函数参数类型,内部做类型守卫
直接把每个相交函数的参数改成Shape类型,在函数内部通过type属性做类型收窄,确保只有匹配的形状类型才执行逻辑:
enum ShapeType { sphere = 0, box = 1, } type Sphere = { type: ShapeType.sphere; radius: number; }; type Box = { type: ShapeType.box; width: number; height: number; depth: number; }; type Shape = Sphere | Box; type IntersectFunction = (a: Shape, b: Shape) => boolean; function intersectSphereSphere(a: Shape, b: Shape): boolean { if (a.type !== ShapeType.sphere || b.type !== ShapeType.sphere) { throw new Error("传入的形状不是两个球体"); } // 这里a和b已被TypeScript自动收窄为Sphere类型 return true; } function intersectBoxBox(a: Shape, b: Shape): boolean { if (a.type !== ShapeType.box || b.type !== ShapeType.box) { throw new Error("传入的形状不是两个立方体"); } return true; } function intersectBoxSphere(a: Shape, b: Shape): boolean { if (a.type !== ShapeType.box || b.type !== ShapeType.sphere) { throw new Error("传入的形状不是立方体和球体"); } return true; } function intersectSphereBox(a: Shape, b: Shape): boolean { return intersectBoxSphere(b, a); } function createIntersectFunctionMap(...mapping: [string, IntersectFunction][]): Map<string, IntersectFunction> { const map = new Map<string, IntersectFunction>(); for (const [key, value] of mapping) { map.set(key, value); } return map; } const map = createIntersectFunctionMap( ["sphere,sphere", intersectSphereSphere], ["box,box", intersectBoxBox], ["box,sphere", intersectBoxSphere], ["sphere,box", intersectBoxSphere] );
方案2:类型断言临时绕过(不推荐)
如果不想修改函数参数,可以在添加到映射时用类型断言强制转换,代价是失去部分类型安全:
const map = createIntersectFunctionMap( ["sphere,sphere", intersectSphereSphere as IntersectFunction], ["box,box", intersectBoxBox as IntersectFunction], ["box,sphere", intersectBoxSphere as IntersectFunction], ["sphere,box", intersectSphereBox as IntersectFunction] );
这种方式能快速解决类型错误,但调用时如果传入不匹配的形状,会直接触发运行时错误,不建议在大型项目中使用。
方案3:类型安全的映射设计(推荐)
通过定义形状类型映射和相交函数映射,让TypeScript自动推导合法的形状对,同时保留类型安全:
enum ShapeType { sphere = 0, box = 1, } type Sphere = { type: ShapeType.sphere; radius: number; }; type Box = { type: ShapeType.box; width: number; height: number; depth: number; }; type Shape = Sphere | Box; // 建立ShapeType到具体形状类型的映射 type ShapeMap = { [ShapeType.sphere]: Sphere; [ShapeType.box]: Box; }; // 定义所有合法的相交函数类型:每个形状对对应专属函数 type IntersectMap = { [K in keyof ShapeMap]: { [L in keyof ShapeMap]: (a: ShapeMap[K], b: ShapeMap[L]) => boolean; }; }; // 实现所有相交逻辑 const intersectImpl: IntersectMap = { [ShapeType.sphere]: { [ShapeType.sphere]: (a, b) => true, [ShapeType.box]: (a, b) => intersectImpl[ShapeType.box][ShapeType.sphere](b, a), }, [ShapeType.box]: { [ShapeType.sphere]: (a, b) => true, [ShapeType.box]: (a, b) => true, }, }; // 自动生成对外的函数映射 function createIntersectFunctionMap(): Map<string, IntersectFunction> { const map = new Map<string, IntersectFunction>(); const shapeTypes = Object.values(ShapeType) as (keyof ShapeMap)[]; for (const typeA of shapeTypes) { for (const typeB of shapeTypes) { const key = `${ShapeType[typeA]},${ShapeType[typeB]}`; map.set(key, (a: Shape, b: Shape) => { if (a.type !== typeA || b.type !== typeB) { throw new Error(`当前映射只支持${key}类型的相交判断`); } return intersectImpl[typeA][typeB](a, b); }); } } return map; } const map = createIntersectFunctionMap();
这种方式既不用手动维护键名和函数的对应关系,又能确保所有相交函数的类型合法性,是最健壮的实现方案。
内容的提问来源于stack exchange,提问作者Nevermore
相关产品推荐
相关产品推荐

