Skip to content

Instantly share code, notes, and snippets.

@pottedmeat
Last active September 22, 2015 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save pottedmeat/acce43a156e989429ae5 to your computer and use it in GitHub Desktop.
Save pottedmeat/acce43a156e989429ae5 to your computer and use it in GitHub Desktop.
function rebase(fn: Function): Function {
return function(...args: any[]) {
return fn.apply(this, [ this ].concat(args));
};
}
const doExtend = rebase(extend);
const doMixin = rebase(mixin);
const doOverlay = rebase(overlay);
function stamp(base: any): void {
base.extend = doExtend;
base.mixin = doMixin;
base.overlay = doOverlay;
}
interface GenericClass<T> {
new (...args: any[]): T;
}
interface ComposerClass<O, T> {
new (options?: O): T;
extend<U>(extension: U): ComposerClass<O, T&U>;
mixin<P, U>(mixin: ComposerClass<P, U>): ComposerClass<O&P, T&U>;
mixin<P, U>(mixin: GenericClass<U>): ComposerClass<O, T&U>;
overlay(overlay: (proto: T) => void): ComposerClass<O, T>;
}
interface ComposerFunction<O> {
(options?: O): void;
}
interface Composer {
<O, A>(superClass: GenericClass<A>, initFunction?: ComposerFunction<O>): ComposerClass<O, A>;
<O, A, P>(superClass: ComposerClass<O, A>, initFunction?: ComposerFunction<P>): ComposerClass<O&P, A>;
<O, A>(superClass: A, initFunction?: ComposerFunction<O>): ComposerClass<O, A>;
create<O, A>(superClass: GenericClass<A>, initFunction?: ComposerFunction<O>): ComposerClass<O, A>;
create<O, A, P>(superClass: ComposerClass<O, A>, initFunction?: ComposerFunction<P>): ComposerClass<O&P, A>;
create<O, A>(superClass: A, initFunction?: ComposerFunction<O>): ComposerClass<O, A>;
extend<O, A, B>(base: ComposerClass<O, A>, extension: B): ComposerClass<O, A&B>;
mixin<O, P, A, B>(base: ComposerClass<O, A>, mixin: ComposerClass<P, B>): ComposerClass<O&P, A&B>;
mixin<O, A, B>(base: ComposerClass<O, A>, mixin: GenericClass<B>): ComposerClass<O, A&B>;
overlay<O, A>(base: ComposerClass<O, A>, overlay: (proto: A) => void): ComposerClass<O, A>;
}
function extend<O, A, B>(base: ComposerClass<O, A>, extension: B): ComposerClass<O, A&B>;
function extend<O>(base: ComposerClass<O, any>, extension: any): ComposerClass<O, any> {
Object.keys(extension).forEach(key => base.prototype[key] = extension[key]);
return base;
}
function mixin<O, P, A, B>(base: ComposerClass<O, A>, mixin: ComposerClass<P, B>): ComposerClass<O&P, A&B>;
function mixin<O, A, B>(base: ComposerClass<O, A>, mixin: GenericClass<B>): ComposerClass<O, A&B>;
function mixin<O>(base: ComposerClass<O, any>, mixin: any): ComposerClass<O, any> {
Object.keys(mixin.prototype).forEach(key => base.prototype[key] = mixin.prototype[key]);
return base;
}
function overlay<O, A>(base: ComposerClass<O, A>, overlay: (proto: A) => void): ComposerClass<O, A> {
overlay(base.prototype);
return base;
}
/* This is a custom type guard, it will narrow the type so you don't have to the cast */
function isFunction(arg: any): arg is Function {
return typeof arg === 'function';
}
function create<O, A>(base: GenericClass<A>, initFunction?: ComposerFunction<O>): ComposerClass<O, A>;
function create<O, A, P>(base: ComposerClass<O, A>, initFunction?: ComposerFunction<P>): ComposerClass<O&P, A>;
function create<O, A>(base: A, initFunction?: ComposerFunction<O>): ComposerClass<O, A>;
function create<O>(base: any, initFunction?: ComposerFunction<O>): any {
function Creator(...args: any[]): void {
if (isFunction(initFunction)) {
initFunction.apply(this, args);
}
}
Creator.prototype = (typeof base === 'function' ? Object.create(base.prototype) : base);
stamp(Creator);
return Creator;
}
stamp(create);
(<Composer> create).create = create;
let compose: Composer = <Composer> create;
class Widget {
buildRendering() {
console.log('Widget.buildRendering');
}
}
class Templated {
render() {
console.log('Templated.render');
}
}
interface Serializable {
toJson()
}
const MyWidget = compose(Widget, function(){
console.log('MyWidget.constructor');
})
.mixin(Templated)
.overlay(function (prototype) {
prototype.buildRendering = function() {
console.log('MyWidget.buildRendering');
this.render();
};
})
.extend(<Serializable>{
toJson() {
return '{"foo":"bar"}';
}
});
const widget = new MyWidget();
widget.buildRendering();
console.log(widget.toJson());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment