Skip to content

Instantly share code, notes, and snippets.

@bolencki13
Created June 10, 2022 19:08
Show Gist options
  • Save bolencki13/95fc574742a29ff4d8f14b7a7c959c66 to your computer and use it in GitHub Desktop.
Save bolencki13/95fc574742a29ff4d8f14b7a7c959c66 to your computer and use it in GitHub Desktop.
Expose promise methods on a class that resolve type T
/**
* Exposes internal promise methods on the class that resolves a result of type T
*/
abstract class PromiseBuilder<T> implements Promise<T> {
/**
* Result to be returned when builder is resolved
*/
protected abstract _fetchResult(): Promise<T>
/**
* Exposes traditional promise methods to the class
*/
then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2> {
const result = this._fetchResult()
return result.then.apply(result, [onfulfilled, onrejected]) as any
}
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult> {
return this.then().catch(onrejected)
}
finally(onfinally?: (() => void) | undefined | null): Promise<T> {
return this.then().finally(onfinally)
}
get [Symbol.toStringTag]() {
return this.constructor.name;
}
}
/**
* Example:
*/
type Pizza = {
crust: 'thin' | 'regular';
cheese: boolean;
sauce: 'none' | 'white' | 'red';
toppings: string[];
}
class PizzaBuilder extends PromiseBuilder<Pizza> {
protected _pizza: Pizza = {
crust: 'regular',
cheese: true,
sauce: 'red',
toppings: []
}
protected _fetchResult(): Promise<Pizza> {
return Promise.resolve(this._pizza)
}
redSauce() {
this._pizza.sauce = 'red'
return this
}
whiteSauce() {
this._pizza.sauce = 'white'
return this
}
noSauce() {
this._pizza.sauce = 'none'
return this
}
cheese(shouldHaveCheese: boolean) {
this._pizza.cheese = shouldHaveCheese
return this
}
thinCrust() {
this._pizza.crust = 'thin'
return this;
}
regularCrust() {
this._pizza.crust = 'regular'
return this;
}
addTopping(topping: string) {
this._pizza.toppings.push(topping);
return this
}
removeTopping(topping: string) {
this._pizza.toppings = this._pizza.toppings.filter((t) => t !== topping);
return this
}
}
async function main() {
let builder = new PizzaBuilder()
builder = builder
.addTopping('pepperoni')
const pizza1 = await builder
console.log(pizza1) // {"crust":"regular","cheese":true,"sauce":"red","toppings":["pepperoni"]}
const pizza2 = await builder
.removeTopping('pepperoni')
.whiteSauce()
.thinCrust()
.addTopping('chicken')
.addTopping('broccoli')
console.log(pizza2) // {"crust":"thin","cheese":true,"sauce":"white","toppings":["chicken","broccoli"]}
}
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment