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)