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

TypeScript工厂函数返回类型推断异常:期望精准类型而非联合类型

解决TypeScript工厂函数返回类型精准推断问题

问题背景

编写的工厂函数接收FactoryOptions<T>类型参数,其中可选的items字段支持两种类型:Item[]数组,或接收默认数组返回新数组的函数(defaultItems: Item[]) => Item[]。但当前函数返回类型总是推断为联合类型,无法根据不同使用场景返回精准类型。

原代码及问题示例:

type Item<T = string> = { value: T };
type DefaultItems = Item<number>[];
type AnyItems = Item<any>[];

type FactoryOptions<T extends AnyItems = DefaultItems> = {
  items?: T | ((defaultItems: DefaultItems) => T);
};

const defaultItems: DefaultItems = [{ value: 1 }, { value: 2 }];
const foo: Item<string> = { value: "foo" };
const num: Item<number> = { value: 3 };

function factory<T extends AnyItems = DefaultItems>(options: FactoryOptions<T> = {}) 
{
    const { items: itemsOrFunc = defaultItems } = options;
    return typeof itemsOrFunc === 'function' ? itemsOrFunc(defaultItems) : itemsOrFunc;
}

// 返回 DefaultItems(符合预期)
const test1 = factory();
// 实际返回:DefaultItems | Item<string>[],期望:Item<string>[]
const test2 = factory({ items: [foo] })
// 实际返回:DefaultItems | Item<number>[],期望:Item<number>[]
const test3 = factory({ items: () => [num] })

解决方案

以下两种方法均可实现每种场景下返回类型的精准推断:

方法一:泛型条件类型推导

通过泛型捕获传入参数的具体类型,结合条件类型判断items是数组还是函数,从而推导精准的返回类型:

function factory<O extends Partial<{ items: AnyItems | ((defaultItems: DefaultItems) => AnyItems) }> = {}>(
  options: O = {} as O
): O extends { items: infer I } 
  ? I extends ((defaultItems: DefaultItems) => infer R) 
    ? R 
    : I 
  : DefaultItems {
  const { items: itemsOrFunc = defaultItems } = options;
  return typeof itemsOrFunc === 'function' ? itemsOrFunc(defaultItems) : itemsOrFunc as any;
}

类型推导逻辑:

  • 若未传入items,返回默认的DefaultItems
  • 若传入items为数组,直接返回该数组的类型
  • 若传入items为函数,返回函数的返回值类型

方法二:函数重载

为三种使用场景分别定义重载签名,让TypeScript根据参数匹配对应的返回类型:

// 重载1:无参数,返回默认数组类型
function factory(): DefaultItems;
// 重载2:传入自定义items数组,返回该数组的类型
function factory<T extends AnyItems>(options: { items: T }): T;
// 重载3:传入生成函数,返回函数的返回值类型
function factory<T extends AnyItems>(options: { items: (defaultItems: DefaultItems) => T }): T;
// 函数实现(类型可宽松处理)
function factory(options: any = {}) {
  const { items: itemsOrFunc = defaultItems } = options;
  return typeof itemsOrFunc === 'function' ? itemsOrFunc(defaultItems) : itemsOrFunc;
}

优势:每个场景对应明确的签名,可读性更强,适合需要清晰区分输入输出的场景。

测试验证

两种方法均可实现预期的类型推断:

  • const test1 = factory() → 类型为DefaultItems
  • const test2 = factory({ items: [foo] }) → 类型为Item<string>[]
  • const test3 = factory({ items: () => [num] }) → 类型为Item<number>[]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:20:15