I'd like to dual-purpose an ES6 class constructor as a mixin function.
I have a simple class with a few methods. A simplified example:
class foo {
constructor() {}
hi() { console.log('hello'); }
}
In most cases I create instances of this class:
let bar = new foo();
bar.hi(); // hello
Sometimes it's used as a superclass:
class baz extends foo {
constructor() { super(); }
}
let bar = new baz();
bar.hi(); // hello
But, as the methods can work independent of anything else, it would be nice if I could use the constructor as a mixin, like so:
class foo {
constructor( mixin ) {
if ( !new.target ) { // not called with `new`
if ( !mixin ) throw new TypeError( 'Must supply mixin' );
mixin.hi = foo.prototype.hi;
return mixin;
}
}
// ...
}
let bar = {}; // this could be instance of another class, whatever.
foo( bar ); // Class constructor foo cannot be invoked without 'new'
bar.hi();
That's the problem I run in to. Constructors can't be invoked as if they were just a normal function.
Is there any way to stop the constructor from throwing an error when invoked without new
, without reverting to an ES5 approach of building classes?
I tried wrapping the class in a normal function, and using new.target
(my env is ES6) to detect when new
is being used:
function Goo( mixin ) {
if ( new.target ) return new foo();
if ( !mixin ) throw new TypeError( 'Must supply mixin' );
mixin.hi = foo.prototype.hi;
return mixin;
}
let goo = new Goo(); // I get a new instance of foo
goo.hi(); // hello
let bar = {};
Goo( bar );
bar.hi(); // hello
...but quickly realised that:
class oof extends Goo { // ugh
Also, I'd have to clone static
stuff from foo
on to Goo
, which is meh.
As a fallback I currently use a static mixin()
method on the foo
class:
class foo {
static mixin( target ) {
if ( !target ) throw new TypeError( 'Must supply target' );
target.hi = foo.prototype.hi;
return target;
}
// ...
}
let bar = {};
foo.mixin( bar ); // Well, it's semantic at least
bar.hi(); // hello
But I'm keen, even just to see if it can be done, to have something that works in these three scenarios:
let ifoo = new foo();
ifoo.hi();
class woo extends foo { /* ... */ }
let iwoo = new woo();
iwoo.hi();
let bar = {};
let ibar = foo( bar );
ibar.hi();
Anyone up for seeing if it's do-able, even if it probably shouldn't be done?