TypeScript中如何从映射变量获取具体类型解决双分派类型报错
报错原因
TS 无法将基类 Shape 的实例类型和其 shapeType 字段做绑定:你逻辑上可以保证当 s1.shapeType 为 ShapeType.Circle 时,s1 一定是 Circle 子类实例,但类型系统无法推导这个关联关系,因此碰撞处理函数的参数类型被推断为 Rectangle & Circle 的交叉类型,传入基类 Shape 自然不匹配。
可行实现方案
这里提供两种常用方案,你可以根据自己的偏好选择:
方案1:保留映射表写法(优先推荐)
通过显式定义映射表类型+合法类型断言解决,完全保留你原本的双分派映射逻辑,只需要补充类型定义即可:
首先先定义所有形状的联合类型,再给映射表声明准确的类型:
// 先定义所有形状的联合类型 type AllShape = Circle | Rectangle; // 定义碰撞映射表的类型,自动关联形状类型和处理函数参数 type ColliderMap = { [T in ShapeType]: { [U in ShapeType]: ( a: Extract<AllShape, { shapeType: T }>, b: Extract<AllShape, { shapeType: U }> ) => boolean; }; }; // 给你的映射表加上上面定义的类型 export let colliderMapping: ColliderMap = { [ShapeType.Rectangle]: { [ShapeType.Rectangle]: handleRectangleRectangleCollision, [ShapeType.Circle]: handleRectangleCircleCollision }, [ShapeType.Circle]: { [ShapeType.Circle]: handleCircleCircleCollision, [ShapeType.Rectangle]: handleCircleRectangleCollision } };
然后修改 doShapesCollide 函数,因为你逻辑上已经保证了映射关系的正确性,可以安全做类型断言:
function doShapesCollide(s1: AllShape, s2: AllShape) { const colliderFn = colliderMapping[s1.shapeType][s2.shapeType] as (a: AllShape, b: AllShape) => boolean; return colliderFn(s1, s2); }
方案2:类型守卫分支判断(零断言完全类型安全)
如果你不想用类型断言,可以通过类型守卫收窄参数类型,TS会自动匹配对应的处理函数:
首先定义两个简单的类型守卫:
function isCircle(s: Shape): s is Circle { return s.shapeType === ShapeType.Circle; } function isRectangle(s: Shape): s is Rectangle { return s.shapeType === ShapeType.Rectangle; }
然后改写碰撞检测函数:
function doShapesCollide(s1: Shape, s2: Shape): boolean { if (isRectangle(s1) && isRectangle(s2)) return handleRectangleRectangleCollision(s1, s2); if (isRectangle(s1) && isCircle(s2)) return handleRectangleCircleCollision(s1, s2); if (isCircle(s1) && isCircle(s2)) return handleCircleCircleCollision(s1, s2); if (isCircle(s1) && isRectangle(s2)) return handleCircleRectangleCollision(s1, s2); throw new Error(`未支持的碰撞类型组合:${s1.shapeType} 和 ${s2.shapeType}`); }
额外注意
你原代码里有个运行时错误:handleCircleCircleCollision 函数调用 circlesCollide 时最后一个参数传成了 c2.y,需要改成 c2.r,否则圆和圆的碰撞检测结果会完全错误。
内容的提问来源于stack exchange,提问作者Ryan Peschel
相关产品推荐
相关产品推荐

