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

显式设置泛型时映射类型无法推断元组元素?有无解决办法?

TypeScript泛型约束与元组类型保留问题

我搞不懂为什么映射类型在显式指定泛型参数时失效,但用默认泛型却能完美推断出准确的参数元组?更关键的是,能不能在约束参数的同时保留默认泛型下的行为?本质上,有没有办法在约束参数的同时不丢失元组类型?

相关类型定义

type Selector<S = any, R = unknown> = (state: S) => R

type SELCTOR_NAME = keyof SELECTOR_COLLECTION

type SELECTOR_COLLECTION = {
    foo: string,
    bar: number
}

type Dependency<S = any> = SELCTOR_NAME | Selector<S>

场景与原函数定义

假设代码库中所有选择器都在SELECTOR_COLLECTION中有对应条目,我们需要实现一个生成缓存选择器的函数,它接收依赖项(可以是SELCTOR_NAME类型的字符串,也可以是Selector函数),返回对应的类型映射结果(为简化问题,这里仅做类型映射演示,实际会返回记忆化选择器)。

原函数定义如下:

declare function cachedSelector<G = any, T extends Dependency[] = Array<Dependency<G>>>(
    ...args: T
): {
        [I in keyof T]: T[I] extends SELCTOR_NAME
        ? () => SELECTOR_COLLECTION[T[I]]
        : T[I]
    }

函数参数的约束逻辑:

  • 参数为SELCTOR_NAME类型时,从SELECTOR_COLLECTION中提取对应类型,返回一个返回该类型的函数
  • 参数为Selector类型时,直接保留该类型

正常工作的情况

当不指定泛型参数时,TypeScript能完美推断元组中每个元素的具体类型:

// result 类型为 [() => string, () => number, () => boolean]
const result = cachedSelector("foo", "bar", () => true)

问题场景

但显式指定泛型参数G(哪怕是默认值any)时,整个类型映射逻辑失效,返回类型退化为Dependency<any>[]:

// result 类型变为 Dependency<any>[]
const result = cachedSelector<any>("foo", "bar", () => true)
// 同时指定两个参数时行为相同
const resultBoth = cachedSelector<any, Dependency<any>[]>("foo", "bar", () => true)

问题原因

显式指定G后,TypeScript会触发T的默认值Array<Dependency<G>>,此时T被推断为数组类型而非具体元组类型。数组的元素类型是宽泛的Dependency<G>,导致条件类型T[I] extends SELCTOR_NAME无法匹配到具体的字符串字面量类型(比如"foo"),最终映射后的类型只能退化为基础约束类型。

解决方案

调整泛型参数的约束方式,去掉T的默认值,让TypeScript自动推断T为具体的元组类型,同时确保每个元素都符合Dependency<G>的约束:

declare function cachedSelector<G = any, T extends Dependency<G>[]>(
    ...args: T
): {
    [I in keyof T]: T[I] extends SELCTOR_NAME
    ? () => SELECTOR_COLLECTION[T[I]]
    : T[I]
}

测试验证

修改后的函数在两种场景下都能正常工作:

// 自动推断:[() => string, () => number, () => boolean]
const result1 = cachedSelector("foo", "bar", () => true)
// 显式指定G后仍保留元组类型:[() => string, () => number, (state: any) => boolean]
const result2 = cachedSelector<any>("foo", "bar", () => true)
// 显式指定具体元组类型也能正常映射
const result3 = cachedSelector<any, ["foo", Selector<any, boolean>]>("foo", () => true)

原理说明

去掉T的默认值后,TypeScript会优先根据传入的参数推断T为具体的元组类型,同时检查元组中的每个元素是否符合Dependency<G>的约束。这样既保留了元组的类型细节,又实现了参数约束,映射类型也能正常匹配每个元素的具体类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 22:04:54