如何为合并泛型对象的函数正确使用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
相关产品推荐
相关产品推荐

