Skip to content

Instantly share code, notes, and snippets.

@emiaj
Created August 1, 2018 15:25
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save emiaj/b7fc92d6fa586935b6f8d62a5299b1e6 to your computer and use it in GitHub Desktop.
Save emiaj/b7fc92d6fa586935b6f8d62a5299b1e6 to your computer and use it in GitHub Desktop.
// 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));
@startswithaj
Copy link

startswithaj commented Nov 23, 2018

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?

const x: RequestSettings = {
  protocol: 'http',
  host: 'test.com',
  path: '/foo/bar',
  headers: []
}

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?

@MAXruppert
Copy link

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