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

如何为TypeScript中fooFunc函数推导目标返回值类型?

如何在TypeScript中定义基于数组元素属性的函数返回类型

要实现你需要的返回类型,得利用TypeScript的类型编程提取数组中每个元素的键值对类型并合并,同时确保TypeScript能推断出具体的键名和值类型(而非宽泛的string/any)。以下是完整解决方案:

完整代码示例

interface A {
    type: 'a',
    option: {
        key: string
        value: any
    }
}

interface B {
    type: 'b' | 'c',
    option: {
        key: string
        secondKey: string
        value: any
        secondValue: any
    }
}

// 用const断言让TypeScript推断出字面量类型,保留具体的键名和值
const foo = [
    {
        type: 'a',
        option: {
            key: 'keyOfA',
            value: 'valueOfA'
        }
    },
    {
        type: 'b',
        option: {
            key: 'keyOfB',
            value: 'valueOfB',
            secondKey: 'secondKeyOfB',
            secondValue: 'secondValueOfB' 
        }
    }
] as const;

// 类型工具:将单个A/B元素转换为对应的键值对类型
type ElementToObj<X extends A | B> = 
    X extends A 
        ? { [K in X['option']['key']]: X['option']['value'] }
        : { 
            [K in X['option']['key'] | X['option']['secondKey']]: 
                K extends X['option']['key'] 
                    ? X['option']['value'] 
                    : X['option']['secondValue'] 
          };

// 类型工具:递归合并数组中所有元素的键值对类型
type MergeArrayElements<T extends readonly (A | B)[]> = 
    T extends readonly [infer First, ...infer Rest]
        ? ElementToObj<First> & MergeArrayElements<Rest>
        : {};

// 类型工具:将交叉类型转换为更友好的普通对象类型
type Flatten<T> = { [K in keyof T]: T[K] };

function fooFunc<T extends readonly (A | B)[]>(foo: T): Flatten<MergeArrayElements<T>> {
    return foo.reduce((prev, cur) => {
        if (cur.type === 'a') {
            return {
                ...prev,
                [cur.option.key]: cur.option.value
            } as Flatten<MergeArrayElements<T>>;
        } else {
            return {
                ...prev,
                [cur.option.key]: cur.option.value,
                [cur.option.secondKey]: cur.option.secondValue
            } as Flatten<MergeArrayElements<T>>;
        }
    }, {} as Flatten<MergeArrayElements<T>>);
}

// 测试:a的类型会被推断为 { keyOfA: "valueOfA"; keyOfB: "valueOfB"; secondKeyOfB: "secondValueOfB" }
const a = fooFunc(foo);

关键步骤解释

  1. 保留字面量类型:给foo添加as const断言,让TypeScript不再把key/value推断为宽泛的string/any,而是保留具体的字面量值(比如'keyOfA'、'valueOfA')。

  2. 类型工具ElementToObj:针对单个A或B类型,将其转换为对应的键值对对象类型:

    • 对于A类型:提取option.key作为键,option.value作为值类型
    • 对于B类型:同时提取option.key和option.secondKey作为键,分别对应各自的value类型
  3. 类型工具MergeArrayElements:递归遍历数组的每个元素,把每个元素对应的ElementToObj类型交叉合并,实现累加的效果。

  4. 类型工具Flatten:把交叉类型(比如{keyOfA: string} & {keyOfB: string})转换为普通对象类型,让类型显示更直观友好。

  5. 函数实现的类型断言:在reduce的返回值和初始值处添加类型断言,告诉TypeScript当前的累加结果符合目标返回类型。

注意事项

如果你的数组元素可能存在重复键名,交叉类型会自动处理(重复键的类型会取交集)。如果需要覆盖重复键的类型,可以调整合并逻辑为替换式(需要更复杂的类型编程)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 22:42:07