如何在TypeScript函数式管道中通过高阶转换器自动推断类型?
问题:让TypeScript自动推断pipe中merge函数的泛型类型
需求背景
我正在构建一个模拟器,使用fp-ts的pipe让对象通过多个函数逐步转换。为提升代码表现力,我采用高阶函数创建转换器,期望代码能写成如下形式:
pipe( { name: "Nick" }, merge({ age: 34 }), merge({ job: "programmer" }), merge({ married: true }) );
理想状态下,通过为pipe指定类型实现类型安全,示例如下:
type Guy = { name: string }; type GuyWithAge = Guy & { age: number }; type GuyWithAgeAndJob = GuyWithAge & { job: string }; type GuyWithAgeAndJobAndMarried = GuyWithAgeAndJob & { married: boolean }; pipe<Guy, GuyWithAge, GuyWithAgeAndJob, GuyWithAgeAndJobAndMarried>( { name: "Nick" }, merge({ age: 34 }), merge({ job: "programmer" }), merge({ married: true }) ); // 最终结果: { name: "Nick", age: 34, job: "programmer", married: true }
我希望TypeScript能自动推断每一步的类型,仅接受类型间的差异,确保对象转换的类型安全。
当前实现与问题
我已通过泛型和TypeScript工具类型实现核心功能,但存在冗余问题:每次调用merge时都需要显式指定泛型类型。代码如下:
// Merge 类似 A & B,但会用B的属性覆盖A的同名属性,等同于 {...A, ...B} type Merge<First extends object, Second extends object> = Omit< First, keyof Second > & Second; // Delta 是与 From 合并后得到 To 的对象类型 export type Delta<From extends object, To extends object> = { [K in keyof To as K extends keyof From ? To[K] extends From[K] ? never : K : K]: To[K]; }; const merge = <First extends object, Second extends object>( delta: Delta<First, Second>, ) => (obj: First): Merge<First, Delta<First, Second>> => { const merged = { ...obj, ...delta, }; return merged; }; type Guy = { name: string }; type GuyWithAge = Guy & { age: number }; type GuyWithAgeAndJob = GuyWithAge & { job: string }; type GuyWithAgeAndJobAndMarried = GuyWithAgeAndJob & { married: boolean }; const guy = pipe<Guy, GuyWithAge, GuyWithAgeAndJob, GuyWithAgeAndJobAndMarried>( { name: "Nick" }, merge<Guy, GuyWithAge>({ age: 34 }), merge<GuyWithAge, GuyWithAgeAndJob>({ job: "programmer" }), merge<GuyWithAgeAndJob, GuyWithAgeAndJobAndMarried>({ married: true }) );
我的问题是:有没有办法让TypeScript自动推断pipe中每个merge调用的正确泛型,从而无需手动指定类型?
补充分析
我进一步拆解了问题:pipe期望接收A => B类型的函数。当传入merge<First, Second>(delta)时,它返回(obj: First) => Merge<First, Delta<First, Second>>。
也就是说,我向pipe提供的函数类型是(obj: First) => Merge<First, Delta<First, Second>>,而pipe需要的是A => B,理论上A应映射到First,B应映射到Merge<First, Delta<First, Second>>(理想情况下可简化为Second,但不确定该简化是否正确)。
我想理解为何TypeScript无法自动推断A对应First、B对应Second,以及如何修复代码以实现预期的自动推断。
内容的提问来源于stack exchange,提问作者Nick Manning
相关产品推荐
相关产品推荐

