Skip to content

Instantly share code, notes, and snippets.

@geovanisouza92
Last active April 3, 2019 11:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save geovanisouza92/1ca74602e92faf433444c7d3540e4516 to your computer and use it in GitHub Desktop.
Save geovanisouza92/1ca74602e92faf433444c7d3540e4516 to your computer and use it in GitHub Desktop.
A builder type where build() only appears after correct initalization
interface Buildable<T> {
build(): T;
}
type HasSawThickness = { sawThickness: number };
type HasDirection = { direction: "horizontal" | "vertical" };
type HasWidth = { width: number };
type HasHeight = { height: number };
type HasPhase = { phase: number };
type Builder<T> = T extends HasSawThickness & HasDirection & HasWidth & HasHeight & HasPhase ? T & Buildable<T> : T;
export function createBuilder(sawThickness: number): Builder<HasSawThickness> {
return {
build: () => {}, // hidden until becoming Buildable<T>
sawThickness,
} as Builder<HasSawThickness>;
}
export function withDirection<T extends HasSawThickness>(it: T, direction: "horizontal" | "vertical"): Builder<T & HasDirection> {
return Object.assign({}, it, { direction }) as Builder<T & HasDirection>;
}
export function withWidth<T extends HasSawThickness>(it: T, width: number): Builder<T & HasWidth> {
return Object.assign({}, it, { width }) as Builder<T & HasWidth>;
}
export function withHeight<T extends HasSawThickness>(it: T, height: number): Builder<T & HasHeight> {
return Object.assign({}, it, { height }) as Builder<T & HasHeight>;
}
export function withPhase<T extends HasSawThickness>(it: T, phase: number): Builder<T & HasPhase> {
return Object.assign({}, it, { phase }) as Builder<T & HasPhase>;
}
const canBuild = withDirection(withPhase(withHeight(withWidth(createBuilder(0), 0), 0), 0), "horizontal");
const cantBuild = withHeight(withWidth(createBuilder(0), 0), 0);
canBuild.build(); // Allowed
cantBuild.build(); // Property 'build' does not exist on type HasSawThickness & HasWidth & HasHeight
class BuilderClass implements Builder<HasSawThickness> {
private _direction: "horizontal" | "vertical";
private _width: number;
private _height: number;
private _phase: number;
constructor(public readonly sawThickness: number) { }
withDirection(direction: "horizontal" | "vertical") {
this._direction = direction;
return this as Builder<this & HasDirection>;
}
withWidth(width: number) {
this._width = width;
return this as Builder<this & HasWidth>;
}
withHeight(height: number) {
this._height = height;
return this as Builder<this & HasHeight>;
}
withPhase(phase: number) {
this._phase = phase;
return this as Builder<this & HasPhase>;
}
protected build() {}
}
const canBuild2 = new BuilderClass(0).withWidth(0).withHeight(0).withPhase(0).withDirection("horizontal");
const cantBuild2 = new BuilderClass(0).withWidth(0).withHeight(0);
canBuild2.build(); // Allowed
cantBuild2.build(); // Property 'build' is protected and only accessible within class 'BuilderClass' and its subclasses
@geovanisouza92
Copy link
Author

geovanisouza92 commented Jun 29, 2018

There's room for improvement, because this use function composition instead a class.


EDIT: Solved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment