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

如何为TypeScript泛型可选函数参数获取正确类型推断?

问题描述

我写了一个合并两个对象的TypeScript函数,当第二个参数bar设为可选时,返回值的类型推断不符合预期,导致可以传入不存在的键值。代码如下:

function combine<F extends Record<string, string>, B extends Record<string, string>>(foo: F, bar?: B) {
    const retVal = {
        ...foo,
        ...bar
    }

    return retVal
}

const test = combine({
    one: 'One',
}, {
    two: 'Two',
})

function testKey<T extends Record<string, string>>(foo: T, bar: keyof T) { }
testKey(test, 'one')   // 合法
testKey(test, 'two')   // 合法
testKey(test, 'three') // 本应不合法,但实际合法

悬停在retVal上时,推断出的类型是:

const retVal: F & {
    [x: string]: string;
}

这个类型限制过于宽松,允许任何字符串作为键。但如果把bar改成非可选参数,retVal的类型就会正确推断为F & B,这才是我想要的结果。现在需要找到在bar为可选参数时,也能得到正确类型推断的方法。


原因分析

TypeScript处理可选对象参数的展开时,因为bar可能是undefined,编译器无法确定它的具体键结构,会退化为宽泛的索引签名类型{[x:string]:string},而不是保留B的具体键信息。


解决方案

方案1:显式指定返回类型

直接将返回类型定义为F & (B | {}),覆盖bar为undefined时的情况(此时相当于合并空对象):

function combine<F extends Record<string, string>, B extends Record<string, string>>(foo: F, bar?: B): F & (B | {}) {
    return {
        ...foo,
        ...bar
    } as F & (B | {})
}

方案2:使用函数重载

通过重载分别定义bar存在和不存在的场景,让编译器精准推断类型:

// 重载1:不传bar时返回F类型
function combine<F extends Record<string, string>>(foo: F): F;
// 重载2:传bar时返回F & B类型
function combine<F extends Record<string, string>, B extends Record<string, string>>(foo: F, bar: B): F & B;
// 实现函数
function combine<F extends Record<string, string>, B extends Record<string, string>>(foo: F, bar?: B) {
    return {
        ...foo,
        ...bar
    }
}

这个方案下,test的类型会被正确推断为{one: string} & {two: string},此时testKey(test, 'three')会直接报错,符合预期。

方案3:条件类型优化

用条件类型区分bar是否传入的情况,进一步精确返回类型:

function combine<F extends Record<string, string>, B extends Record<string, string> = {}>(
    foo: F, 
    bar?: B
): B extends {} ? F & B : F {
    return {
        ...foo,
        ...bar
    } as any
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 04:45:18