Skip to content

Instantly share code, notes, and snippets.

@danieldietrich
Last active February 2, 2020 09:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danieldietrich/dd352b89b00f4a8e77be01ab1ae22385 to your computer and use it in GitHub Desktop.
Save danieldietrich/dd352b89b00f4a8e77be01ab1ae22385 to your computer and use it in GitHub Desktop.
A generic functional object builder. Builders are used in the presence of default values or if objects are created in multiple steps. Otherwise you would create them directly using the object literal {}.
type Builder<T> = {
with: (t: Partial<T>) => Builder<T>,
build: () => T
}
// A generic builder that works for *ALL* object types
function builder<T>(options: { defaultValues: T }): Builder<T> {
return {
with: _with,
build: () => ({...options.defaultValues})
};
function _with(t1: Partial<T>): Builder<T> {
return {
with: t2 => _with({...t1, ...t2}),
build: () => ({...options.defaultValues, ...t1})
};
}
}
// Example application
type Person = {
forename: string
surname: string
phone?: number
address?: Address[]
}
type Address = {
street?: string
zip?: number
city?: string
country?: 'foo' | 'bar' | 'baz'
}
const personBuilder = builder<Person>({ defaultValues: { forename: "(unknown)", surname: "(unknown)" }});
const addressBuilder = builder<Address>({ defaultValues: {}});
// inferred type: Person
const daniel = personBuilder
.with({ forename: 'Daniel', surname: 'Dietrich' }) // setting multiple attributes at once
.with({ phone: 123 })
.with({ address: [
addressBuilder.with({ street: 'Milkyway', country: 'foo' }).build(), // setting only some optional attributes
addressBuilder.with({ street: 'Elmstreet', country: 'bar' }).build()
]})
.build();
{
forename: 'Daniel',
surname: 'Dietrich',
phone: 123,
address: [
{ street: 'Milkyway', country: 'foo' },
{ street: 'Elmstreet', country: 'bar' }
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment