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

如何修改fpShallowMerge类型定义,让TS自动推断泛型T为State?

问题描述

现有以下TypeScript代码:

export type Resolvable<TValue = unknown, TArgs extends ReadonlyArray<unknown> = []> = TValue | ((...args: TArgs) => TValue)

export function fpShallowMerge<T>(...objects: Array<{
    [K in keyof T]?: Resolvable<T[K], [T[K], K]>;
}>): (obj: T) => T & {} {
    return {} as any
}

type State = {
    scale: number
    xOffset: number
    yOffset: number
}
let state: State = {
    scale: 0,
    xOffset: 0,
    yOffset: 0,
}
const setState = (fn: (prev: State) => State) => {
    state = fn(state)
}

setState(fpShallowMerge<State>({xOffset: 2, yOffset: 3}))  // 正常工作
setState(fpShallowMerge({xOffset: 2, yOffset: 3}))  // 无法推断State类型

手动指定泛型<State>时代码正常运行,但省略泛型参数后,TypeScript无法自动推断出T为State类型。需要修改fpShallowMerge的类型定义,让TypeScript能从setState的参数类型((prev: State) => State)反向推断出T的类型。

解决方案

核心思路是调整泛型推断方向:让TypeScript优先从返回函数的使用上下文(即setState要求的参数类型)推断T,而非仅从传入的objects参数推断。

我们可以将fpShallowMerge改为返回一个泛型函数,让该函数的泛型参数T由上下文自动推断,同时在实现中对objects做类型断言以匹配T的结构:

export type Resolvable<TValue = unknown, TArgs extends ReadonlyArray<unknown> = []> = TValue | ((...args: TArgs) => TValue)

export function fpShallowMerge(...objects: Array<{
    [K in keyof any]?: Resolvable<any, [any, K]>;
}>): <T extends object>(obj: T) => T {
    return (obj: T) => {
        // 将objects断言为符合T结构的类型
        const validObjects = objects.filter(o => o != null) as Array<{
            [K in keyof T]?: Resolvable<T[K], [T[K], K]>;
        }>
        
        if (!validObjects.length) {
            return obj
        }
        
        const ret = Object.assign(Object.create(null), obj)
        for (const o of validObjects) {
            for (const k of Object.keys(o!) as (keyof T)[]) {
                const value = o![k]
                // 处理Resolvable类型:如果是函数则执行,否则直接赋值
                ret[k] = typeof value === 'function' 
                    ? (value as (...args: [T[K], keyof T]) => T[K])(ret[k], k) 
                    : value
            }
        }
        return ret
    }
}

修改后,调用setState(fpShallowMerge({xOffset: 2, yOffset: 3}))时,TypeScript会根据setState要求的(prev: State) => State自动推断出泛型T为State,无需手动指定。

完整实现代码
export type Resolvable<TValue = unknown, TArgs extends ReadonlyArray<unknown> = []> = TValue | ((...args: TArgs) => TValue)

export function fpShallowMerge(...objects: Array<{
    [K in keyof any]?: Resolvable<any, [any, K]>;
}>): <T extends object>(obj: T) => T {
    return (obj: T) => {
        const validObjects = objects.filter(o => o != null) as Array<{
            [K in keyof T]?: Resolvable<T[K], [T[K], K]>;
        }>
        
        if (!validObjects.length) {
            return obj
        }
        
        const ret = Object.assign(Object.create(null), obj)
        for (const o of validObjects) {
            for (const k of Object.keys(o!) as (keyof T)[]) {
                const value = o![k]
                ret[k] = typeof value === 'function' 
                    ? (value as (...args: [T[K], keyof T]) => T[K])(ret[k], k) 
                    : value
            }
        }
        return ret
    }
}

// 使用示例
type State = {
    scale: number
    xOffset: number
    yOffset: number
}
let state: State = {
    scale: 0,
    xOffset: 0,
    yOffset: 0,
}
const setState = (fn: (prev: State) => State) => {
    state = fn(state)
}

// 现在无需手动指定泛型,TypeScript自动推断T为State
setState(fpShallowMerge({xOffset: 2, yOffset: 3}))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 13:55:38