基于fp-ts实现类flow/pipe的对象属性合并函数流
基于fp-ts实现增量对象管道
核心需求回顾
我们需要实现一个类似pipe的工具,但针对对象做增量处理:
- 每个函数接收当前累计的合并对象,仅返回自己新增的属性
- 工具自动将函数的输入与输出合并,传递给下一个函数
- 类型层面保证安全,同时允许函数仅使用累计对象的部分属性(不触发类型错误)
fp-ts实现方案
利用fp-ts的pipe和类型工具,结合TypeScript的类型推导来实现简洁的函数式方案:
类型定义
先定义核心类型,关联管道中函数的输入输出关系:
import { pipe } from 'fp-ts/function'; // 提取对象键值,用于类型推导 type ObjectKeys<T> = keyof T; // 管道中单个函数的类型:接收累计对象S,返回新增属性A type ObjectPipeFn<S, A extends Record<string, unknown>> = (s: S) => A; // 递归推导管道执行后的最终合并类型 type ObjectPipeFinal<Fns extends readonly ObjectPipeFn<any, any>[], Acc = {}> = Fns extends [infer First, ...infer Rest] ? First extends ObjectPipeFn<Acc, infer A> ? ObjectPipeFinal<Rest, Acc & A> : Acc : Acc;
核心管道实现
实现objectPipe,自动处理对象合并,同时保证类型安全:
// 基础合并函数:将累计对象与新增属性合并 const merge = <S, A extends Record<string, unknown>>(base: S, addition: A): S & A => ({ ...base, ...addition }); // 构建对象管道:接收函数数组,返回可接收初始对象的执行函数 const objectPipe = <Fns extends readonly ObjectPipeFn<any, any>[]>( ...fns: Fns ) => <S extends Record<string, unknown>>(initial: S): S & ObjectPipeFinal<Fns> => pipe( initial, ...fns.map(fn => (current: any) => merge(current, fn(current))) );
容错性优化(支持函数仅依赖部分属性)
为了让函数可以仅使用累计对象的部分属性而不触发类型错误,我们可以调整函数类型,让其输入兼容累计对象的子集:
// 优化后的函数类型:允许函数接收累计对象的任意子集 type ObjectPipeFn<Input extends Record<string, unknown>, Output extends Record<string, unknown>> = (s: Input) => Output; // 此时,当累计对象是A&B&C时,函数仅声明接收A&C,TypeScript会自动兼容类型
使用示例
// 示例函数:各自仅返回新增属性 const addName = (s: {}) => ({ name: "Alice" }); const addAge = (s: { name: string }) => ({ age: 30 }); // 仅使用name属性,忽略已存在的age const addEmail = (s: { name: string }) => ({ email: `${s.name}@example.com` }); // 创建管道 const buildUser = objectPipe(addName, addAge, addEmail); // 执行管道,传入初始空对象 const user = buildUser({}); // user类型自动推导为:{ name: string; age: number; email: string }
方案优势
- 完全基于fp-ts的
pipe实现,符合函数式编程风格 - 自动处理对象合并,调用方无需手动合并输入输出
- 类型推导准确,同时支持函数仅依赖累计对象的部分属性,具备良好的容错性
内容的提问来源于stack exchange,提问作者NSjonas
相关产品推荐
相关产品推荐

