Multiple inheritance is possible in plain JavaScript. Type support in Hegel?
Hello! At the moment, I am able to use Proxy to implement a function called multiple() (with its unit tests) that can be used like this:
import {multiple} from 'lowclass'
class Foo {
foo() {console.log('foo')}
}
class Yeah {
yeah() {return 'yeah'}
}
class Bar extends Yeah {
bar() {console.log('bar' + super.yeah())}
}
class Baz extends multiple(Foo, Bar, Yeah) { // extra Yeah is ignored
baz() {console.log('baz ' + super.yeah())}
}
const b = new Baz
// it works!
b.foo() // logs "foo"
b.yeah() // returns "yeah"
b.bar() // logs "bar yeah"
b.baz() // logs "baz yeah"
In plain JavaScript this works great and is much more convenient than class-factory mixins. I run into some troubles with the types in TypeScript, mostly because features like protected and private can not be used in mapped types.
Hegel doesn't support protected, so no issue there, but Hegel doesn't have the same types as TS, so not sure how to do this yet.
See CombinedClasses type of my multiple.ts.
Here's what that type looks like currently in Hegel Try. And the code:
type CombinedClasses<T> = T extends [] | [undefined]
? typeof Object
: T extends Constructor[]
? MixedArray<T>
: typeof Object
Is is possible to achieve it in Hegel?
I would love to be able to do this in Hegel!
For now, answer is No. Because inheritance can be only with one class (because of prototype).
But for types (interfaces) we already have multiple inheritance.
Thank you for the question ^_^
If you're curious about multiple(), it basically creates a virtual prototype tree. The internal Proxy traps fork the property lookup onto each class's prototype chains, and apply the correct receiver arguments to the underlying Reflect calls.
The end result is simple for the end user: it allows them to combine any classes they want, thus allow code organization to not be restricted by stiff and difficult-to-modify class hierarchies.
A perfect use case example is something like mixing a utility class onto an existing class. For example, a project might contain this code
import {BaseClass} from './somewhere'
class MyClass extends BaseClass { /* ... */ }
and the user would like to make it also emit events, and there's already the Node.js builtin module that exports EventEmitter, so it can be re-used:
import {multiple} from 'lowclass'
import {EventEmitter} from 'events' // <-- Node.js builtin module, it has no idea about the existence of the multiple() function
import {BaseClass} from './somewhere'
class MyClass extends multiple(EventEmitter, BaseClass) {
/* ... now the class can emit events too ... */
}
Class-factory mixins, on the other hand, have to be pre-designed as such. With multiple() we can mix any classes, without having to convert them to class-factory mixins.
Man, I daydream about how awesome it would be to have this multiple() inheritance be strictly typed.