You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 03:04:50