Skip to content

Instantly share code, notes, and snippets.

@PanagiotisPtr
Created January 26, 2022 20:35
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 PanagiotisPtr/14ee4da458273cfda769a7b74be27c59 to your computer and use it in GitHub Desktop.
Save PanagiotisPtr/14ee4da458273cfda769a7b74be27c59 to your computer and use it in GitHub Desktop.
Solution to exercise 4.b from Chapter 5 of "Programming TypeScript"
/**
* Create a class that uses the builder pattern and only allows the 'send' method to be called only
* when both setUrl and setMethod have been called before it (in any order)
*/
interface WithUrl {
getUrl(): string;
setMethod(method: string): ReadyToSend;
}
interface WithMethod {
getMethod(): string;
setUrl(url: string): ReadyToSend;
}
interface ReadyToSend extends WithUrl, WithMethod {
send(): void;
}
type ClassConstructor<T = any> = {
new(...args: any[]): T
}
const WithUrl = <
T extends ClassConstructor
>(Class: T, url: string) => {
return class extends Class implements WithUrl {
getUrl(): string {
return url;
}
setMethod(method: string): ReadyToSend {
const BuilderWithMethod = WithMethod(
Object.getPrototypeOf(this).constructor,
method
);
const FinalBuilder = WithMethodAndUrl(
BuilderWithMethod
);
return new FinalBuilder();
}
}
}
const WithMethod = <
T extends ClassConstructor
>(Class: T, method: string) => {
return class extends Class implements WithMethod {
getMethod(): string {
return method;
}
setUrl(url: string): ReadyToSend {
const BuilderWithUrl = WithUrl(
Object.getPrototypeOf(this).constructor,
url
);
const FinalBuilder = WithMethodAndUrl(
BuilderWithUrl
);
return new FinalBuilder();
}
}
}
const WithMethodAndUrl = <
T extends ClassConstructor<WithUrl & WithMethod>
>(Class: T) => {
return class extends Class implements ReadyToSend {
send(): void {
console.log(this.getUrl(), this.getMethod());
}
}
}
class Builder {
setUrl(url: string): WithUrl {
const BuilderWithUrl = WithUrl(Builder, url);
return new BuilderWithUrl();
}
setMethod(method: string): WithMethod {
const BuilderWithUrl = WithMethod(Builder, method);
return new BuilderWithUrl();
}
}
let a = new Builder();
a.setUrl('someUrl').setMethod('someMethod').send();
let b = new Builder();
b.setMethod('someOtherMethod').setUrl('someOtherUrl').send();
let c = new Builder();
// c.setUrl('url').send(); // error
// c.setMethod('method').send(); // error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment