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

如何为合并泛型对象的函数正确使用TypeScript泛型类型?

问题描述

我要实现一个Hook,它接收名为defaultParams的对象,返回一个接收allParams的函数,最终返回合并后的对象:当allParams中的值为null或undefined时,使用defaultParams中的对应值。目前功能正常,但返回值的类型不准确,比如下面代码中count、pet和firstName的类型不符合预期:

代码示例
interface Person {
    firstName?: string
    count?: number
    pet?: boolean
}

type RequiredPick<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>

const useDefaultParams = <T, DKey extends keyof T = keyof T>(defaultParams: { [K in DKey]?: NonNullable<T[K]> }) => {
    return (allParams: T) => {
        const allParamsWithDefaults = { ...allParams } as Partial<T>

        Object.entries(defaultParams).forEach(([key, value]) => {
            (allParamsWithDefaults as any)[key] ??= value!
        })
        return allParamsWithDefaults as RequiredPick<T, DKey>
    }
}

const defaultParams = { count: 10 }

const { count, pet, firstName } = useDefaultParams<Person>(defaultParams)({ firstName: 'firstName' })
// 期望:typeof pet 是 undefined
// 期望:typeof count 是 number
// 期望:typeof firstName 是 string
解决方案

问题出在类型定义的推导逻辑上,原有的RequiredPick无法精准结合allParams的实际传入值和defaultParams的默认值。通过调整泛型参数和返回类型,可以让TypeScript准确推导最终合并对象的类型:

interface Person {
    firstName?: string
    count?: number
    pet?: boolean
}

// 定义合并后的类型:将defaultParams中的键转为必填非空类型,其余保持原类型
type MergedWithDefaults<T, D extends Partial<T>> = {
    [K in keyof T]: K extends keyof D ? NonNullable<T[K]> : T[K]
};

const useDefaultParams = <T, D extends Partial<T>>(defaultParams: D) => {
    // 接收allParams,同时推导其具体传入的属性类型
    return <P extends Partial<T>>(allParams: P): MergedWithDefaults<T, D> & P => {
        const merged = { ...allParams } as MergedWithDefaults<T, D> & P;
        
        // 遍历defaultParams,为null/undefined的属性赋值默认值
        Object.entries(defaultParams).forEach(([key, value]) => {
            const typedKey = key as keyof T;
            if (merged[typedKey] == null) {
                merged[typedKey] = value as MergedWithDefaults<T, D>[typeof typedKey];
            }
        });
        
        return merged;
    };
};

const defaultParams = { count: 10 };

const { count, pet, firstName } = useDefaultParams<Person>(defaultParams)({ firstName: 'firstName' });
// typeof count: number(符合预期)
// typeof firstName: string(符合预期)
// typeof pet: boolean | undefined(符合预期)

关键优化点:

  • 使用D extends Partial<T>自动推导defaultParams的键,无需手动指定DKey
  • 定义MergedWithDefaults类型,明确将defaultParams中的键转为必填非空类型,其余键保持原类型
  • 返回类型结合MergedWithDefaults<T, D>和传入的allParams类型P,确保传入的具体属性类型被正确保留

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 16:32:51