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

TypeScript中Props<T>[K]解析为联合类型的原因及优化问询

TypeScript泛型映射类型的类型收窄问题

先看我们的代码实现:

interface SubProps1<T> {
    foo: boolean
    bar: string
    baz: T
}

interface SubProps2<T> {
    fooz: boolean
    barr: string
    bazd: Array<T>
}

interface Props<T> {
    sub1: SubProps1<T>
    sub2: SubProps2<T>
}

type CSSObjectWithLabel = Record<string, unknown>

type StylesConfig<T> = {
    [K in keyof Props<T>]?: StylesConfigFunction<Props<T>[K]>;
}

type StylesConfigFunction<SubProps> =
    (base: CSSObjectWithLabel, props: SubProps) => CSSObjectWithLabel;

function mergeStyles<T>(
    nonColorStyles: StylesConfig<T>,
    colorStyles: StylesConfig<T>,
): StylesConfig<T> {

    type S = StylesConfig<T>

    const result: S = {};

    const uniqueKeys = new Set([
        ...Object.keys(nonColorStyles),
        ...Object.keys(colorStyles)
    ] as (keyof S)[])

    function assigner<K extends keyof S>(key: K) {
        // 这里Props<T>[K]被解析为SubProps1<T> | SubProps2<T>联合类型,导致类型不匹配
        result[key] = (base: CSSObjectWithLabel, props: Props<T>[K]): CSSObjectWithLabel => ({
            ...base,
            ...colorStyles[key]?.(base, props),
            ...nonColorStyles[key]?.(base, props)
        })
    }

    return result;
}

问题说明

尝试合并符合Props接口的键值映射函数时,assigner函数内为result[key]赋值时,Props<T>[K]被解析为SubProps1<T> | SubProps2<T>联合类型。理论上特定key对应的result[key]类型应该匹配对应的StylesConfigFunction,只接受正确的SubProps类型而非联合类型,但TypeScript无法自动做到这一点。

限制原因

TypeScript的类型系统在处理泛型索引类型时,无法跟踪泛型参数K和具体属性类型的一一对应关系:

  • K是一个泛型参数,代表keyof S的任意子集,而非某个具体的键
  • 当在函数内部使用key: K时,TypeScript只能推断出Props<T>[K]是所有可能的SubProps类型的联合,无法将K的具体值和对应的属性类型绑定
  • 这是TypeScript类型系统的设计限制,为了保证类型检查的性能和复杂度可控,它不会对泛型索引做更细粒度的关联推断

优化方案

方案一:显式类型断言

直接将赋值的函数断言为StylesConfig<T>[K],明确告诉TypeScript我们确认类型的正确性:

function assigner<K extends keyof S>(key: K) {
    result[key] = ((base: CSSObjectWithLabel, props: Props<T>[K]) => ({
        ...base,
        ...colorStyles[key]?.(base, props),
        ...nonColorStyles[key]?.(base, props)
    })) as StylesConfig<T>[K];
}

方案二:显式声明函数类型

先声明合并后的函数类型为StylesConfigFunction<Props<T>[K]>,让TypeScript明确参数与返回值的类型匹配关系:

function assigner<K extends keyof Props<T>>(key: K) {
    const mergedFn: StylesConfigFunction<Props<T>[K]> = (base, props) => ({
        ...base,
        ...colorStyles[key]?.(base, props),
        ...nonColorStyles[key]?.(base, props)
    });
    result[key] = mergedFn;
}

方案三:使用reduce简化合并逻辑

通过reduce遍历键,结合类型断言完成合并,代码更简洁:

function mergeStyles<T>(
    nonColorStyles: StylesConfig<T>,
    colorStyles: StylesConfig<T>,
): StylesConfig<T> {
    const uniqueKeys = new Set([
        ...Object.keys(nonColorStyles),
        ...Object.keys(colorStyles)
    ] as (keyof Props<T>)[])

    return [...uniqueKeys].reduce((acc, key) => {
        acc[key] = (base: CSSObjectWithLabel, props) => ({
            ...base,
            ...colorStyles[key]?.(base, props as Props<T>[typeof key]),
            ...nonColorStyles[key]?.(base, props as Props<T>[typeof key])
        }) as StylesConfig<T>[typeof key];
        return acc;
    }, {} as StylesConfig<T>);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 23:00:39