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

TypeScript重载箭头函数实现报错的排查与解决

问题描述

我尝试创建一个工厂函数,它接受类型为{ item: T; weight: number }的数组,返回带有pick方法的对象。pick方法根据是否传入定义了quantity的参数,返回T或T[],类型定义如下:

type PickOneOptions = {
  quantity: undefined;
};

type PickManyOptions = {
  quantity: number;
};

type PickOptions = PickOneOptions | PickManyOptions;

type WeightedItem<T> = { item: T; weight: number };

type WeightedTable<T> = {
  pick: {
    (): T;
    (options: PickOneOptions): T;
    (options: PickManyOptions): T[];
  };
};

但在实现该工厂函数时,TypeScript报错:'T' could be instantiated with an arbitrary type which could be unrelated to 'T | T[]'。虽然使用类型断言可以消除错误,且实际调用时返回值符合预期:

const createWeightedTable = <T>(items: WeightedItem<T>[]): WeightedTable<T> => {
  const totalWeight = items.reduce((prev, curr) => prev + curr.weight, 0);

  return {
    pick: (options?: PickOptions) => { // 报错位置
      const { quantity } = options ?? {};
      const weightedItems = items;

      let random = Math.random() * totalWeight;

      if (quantity === undefined) {
        for (const weightedItem of weightedItems) {
          random -= weightedItem.weight;

          if (random <= 0) {
            return weightedItem.item;
          }
        }

        throw new Error();
      }

      const result: T[] = [];

      for (let i = 0; i < quantity; i++) {
        for (const weightedItem of items) {
          random -= weightedItem.weight;

          if (random <= 0) {
            result.push(weightedItem.item);
          }
        }
      }

      return result;
    },
  };
};

const table = createWeightedTable([
  {
    item: 'a',
    weight: 1
  },
  {
    item: 'b',
    weight: 1
  }
])

const resultOne = table.pick(); // string

const resultMany = table.pick({ quantity: 2 }); // string[]
报错原因

TypeScript无法自动将实现的pick函数返回类型与WeightedTable<T>中定义的重载签名匹配。你的实现函数被推断为返回T | T[],但重载签名要求不同参数对应明确的单一返回类型(无参数/PickOneOptions返回T,PickManyOptions返回T[])。

当泛型T被实例化为任意类型时(比如T本身就是数组类型),T | T[]会出现类型重叠,TypeScript无法保证这个联合类型能准确对应重载的返回要求,因此抛出错误。

解决方法

无需类型断言,通过精确类型收窄让TypeScript明确每个分支的返回类型:

调整pick函数内的逻辑,直接针对options进行类型检查,而不是解构出quantity后再判断,这样TypeScript能将options的类型收窄到对应的PickOneOptions或PickManyOptions:

const createWeightedTable = <T>(items: WeightedItem<T>[]): WeightedTable<T> => {
  const totalWeight = items.reduce((prev, curr) => prev + curr.weight, 0);

  return {
    pick: (options?: PickOptions) => {
      let random: number;

      // 直接检查options,让TypeScript收窄类型
      if (!options || options.quantity === undefined) {
        random = Math.random() * totalWeight;
        for (const weightedItem of items) {
          random -= weightedItem.weight;
          if (random <= 0) {
            return weightedItem.item;
          }
        }
        throw new Error("No item selected");
      } else {
        const result: T[] = [];
        // 每次循环重新生成随机数,避免重复选择同一结果
        for (let i = 0; i < options.quantity; i++) {
          random = Math.random() * totalWeight;
          for (const weightedItem of items) {
            random -= weightedItem.weight;
            if (random <= 0) {
              result.push(weightedItem.item);
              break; // 找到结果后跳出内层循环
            }
          }
        }
        return result;
      }
    },
  };
};

注:原批量选择逻辑存在bug(随机数仅生成一次,会导致多次选择同一结果),上述代码已顺带修复该问题。

内容的提问来源于stack exchange,提问作者Tiago

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:53:12