如何修改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
相关产品推荐
相关产品推荐

