You need to enable JavaScript to run this app. TypeScript Map getOrCreate 辅助函数类型不健全问题处理方案咨询
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript Map getOrCreate 辅助函数类型不健全问题处理方案咨询

问题1:更优的实现方案

最优的解决方式是阻止TypeScript从valueFn参数推导泛型V的类型,强制仅从传入的Map实例推导V,从根源避免协变导致的类型放宽问题:

  • 如果你使用 TypeScript 5.4 及以上版本,可以直接用内置的NoInfer工具类型实现,修改后的代码如下:
export function mapGetOrCreate<K, V>(map: Map<K, V>, key: K, valueFn: (key: K) => NoInfer<V>): V {
    let value = map.get(key);
    if (value === undefined) {
        value = valueFn(key);
        assert(value !== undefined);
        map.set(key, value);
    }
    return value;
}

这个版本在你的测试用例中调用mapGetOrCreate(m, '1', createA)会直接抛出类型错误,完全符合预期。

  • 如果你使用低于5.4的TypeScript版本,可以自行实现NoInfer,兼容性实现代码如下:
type NoInfer<T> = [T][T extends any ? 0 : never];

除此之外,你也可以用柯里化的写法拆分函数参数,先固定Map的类型:

export function mapGetOrCreate<K, V>(map: Map<K, V>) {
    return function(key: K, valueFn: (key: K) => V): V {
        let value = map.get(key);
        if (value === undefined) {
            value = valueFn(key);
            assert(value !== undefined);
            map.set(key, value);
        }
        return value;
    }
}
// 调用方式为 mapGetOrCreate(m)('1', createA),同样会触发正确的类型报错

问题2:mapGetOrCreate2的劣势

你修改后的版本确实能解决自动推导场景下的类型漏洞,但存在三个明显的劣势:

  1. 冗余泛型增加使用成本:多出来的V2参数没有实际业务意义,如果遇到需要手动指定泛型的场景,需要多填一个参数,提升了使用门槛。
  2. 本质漏洞未修复:只要手动指定泛型参数mapGetOrCreate2<string, B, A>(m, '1', createA),仍然可以绕开类型检查,没有从根源解决型变导致的不健全问题。
  3. 返回值丢失子类型信息:如果valueFn返回的是V的子类型,这个版本的返回值固定为V,无法自动推导子类型的属性,后续使用子类型独有属性时需要额外做类型断言,增加了不必要的样板代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 08:45:02