Long story short - object builder pattern in TypeScript :)
type Builder<Source extends object, Target> = { [Prop in keyof Required<Source>]: (value: Source[Prop]) => Builder<Source, Target> } & { build(): Target }
This type defines an object that will have methods of the same name as the Source
object plus the build()
method.
Here's an implementation thereof using Proxy:
function clone<S, T>(source: S): T { return JSON.parse(JSON.stringify(source)) } function builder< Source extends object, Target >( source: Source, convert: (source: Source) => Target = clone ): Builder<Source, Target> { const proxy = new Proxy<Builder<Source, Target>>( source as any, { get(target, prop: string, receiver) { if (prop === 'build') return build else return (value: any) => setter( prop as keyof Source, value ) } }); function build(): Target { return convert(source) } function setter( prop: keyof Source, value: any ): Builder<Source, Target> { source[prop] = value return proxy } return proxy }
And here's how you could use it:
const b = builder( { x: 1, y: 'Hello' }, source => ({ o1: { a: source.y, b: source.x }, o2: { p1: source.x, p2: source.y }, }) ) console.log(b.x(3).y('2').build())
Each call to the builder methods is type-safe so it should be easy to work with it :)
Happy coding!
Top comments (0)