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(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment