Created
August 1, 2018 15:25
-
-
Save emiaj/b7fc92d6fa586935b6f8d62a5299b1e6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Builders | |
class SimpleBuilder { | |
constructor(private current = {}) { | |
} | |
prop(key: string, value: any) { | |
return new SimpleBuilder({ ...this.current, ...{ [key]: value } }); | |
} | |
build<R>() { | |
return <R>this.current; | |
} | |
} | |
class TypedBuilder<T> { | |
constructor(private current = {}) { | |
} | |
prop<P extends keyof T, V extends T[P]>(key: P, value: V) { | |
return new TypedBuilder<T>({ ...this.current, ...{ [key]: value } }); | |
} | |
build() { | |
return <T>this.current; | |
} | |
} | |
class AdvanceBuilder<T, R extends {} = {}> { | |
constructor(private current: R = null) { | |
} | |
// P: Only those properties from T that do not exist in R | |
prop<P extends Exclude<keyof T, keyof R>, V extends T[P]>(key: P, value: V) { | |
let extra: Pick<T, P> = { [key]: value }; | |
// `instance` is an intersection between our accumulator type (R) and | |
// the `extra` object created above | |
let instance = { | |
...(this.current as object), | |
...extra | |
} as R & Pick<T, P>; | |
return new AdvanceBuilder<T, R & Pick<T, P>>(instance); | |
} | |
build(): R { | |
return this.current; | |
} | |
} | |
// Domain | |
interface RequestSettings { | |
protocol: 'http' | 'https'; | |
host: string; | |
path: string; | |
query?: string; | |
headers: { key: string, value: string }[] | |
} | |
// Usage | |
const settings1 = new SimpleBuilder() | |
.prop('protocol', 'http') | |
.prop('host', 'test.com') | |
.prop('path', '/foo/bar') | |
.prop('headers', []) | |
.build<RequestSettings>(); | |
const settings2 = new TypedBuilder<RequestSettings>() | |
.prop('protocol', 'http') | |
.prop('host', 'test.com') | |
.prop('path', '/foo/bar') | |
.prop('headers', []) | |
.build(); | |
const settings3: RequestSettings = new AdvanceBuilder<RequestSettings>() | |
.prop('protocol', 'http') | |
.prop('host', 'test.com') | |
.prop('path', '/foo/bar') | |
.prop('headers', []) | |
.build(); | |
// Logging | |
alert(JSON.stringify(settings1)); | |
alert(JSON.stringify(settings2)); | |
alert(JSON.stringify(settings3)); |
Really enjoyed this utility of typescript generics. Thanks!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey Jaime, I was at the angular meet up the other night and saw you talk on these builders. Do you have any real world usage examples?
What would be the advantage of using these instead of conventionally creating a typed object?
Also in your example on the night one of your builders had an
extend
method that allowed you to pass an object of {k:v}. Do you have that code handy?