Skip to content

Instantly share code, notes, and snippets.

@SebastiaanYN
Forked from junlarsen/js-multi-inheritance.ts
Last active November 11, 2019 16:26
Show Gist options
  • Save SebastiaanYN/72789494eb5bf1785ec9554d7ac7ed92 to your computer and use it in GitHub Desktop.
Save SebastiaanYN/72789494eb5bf1785ec9554d7ac7ed92 to your computer and use it in GitHub Desktop.
Implementation of multi inheritance output for a transpiler in JS
interface Parents {
[parent: string]: Class;
}
abstract class Class {
$child?: Class;
readonly $parents: Parents;
constructor($parents: Parents) {
this.$parents = $parents;
}
$instanceof(clazz: any): boolean {
if (clazz instanceof this.constructor) {
return true;
}
for (const parent of Object.values(this.$parents)) {
if (parent.$instanceof(clazz)) {
return true;
}
}
return false;
}
$callSelf(method: string, params: any[]): any {
return (this as any)[method](...params);
}
$callUp(method: string, params: any[]): any {
let highest: Class | undefined;
let current: Class = this;
// Find highest child with the given method name
while (current.$child) {
current = current.$child;
if (current.$has(method)) {
highest = current;
}
}
if (highest) {
return highest.$callSelf(method, params);
}
if (this.$has(method)) {
return this.$callSelf(method, params);
}
return this.$callDown(method, params);
}
$callDown(method: string, params: any[]): any {
if (this.$has(method)) {
return this.$callSelf(method, params);
}
let parents = Object.values(this.$parents);
while (parents.length) {
for (let i = 0; i < parents.length; i += 1) {
if (parents[i].$has(method)) {
return parents[i].$callSelf(method, params);
}
}
const newParents: Class[] = [];
for (let i = 0; i < parents.length; i += 1) {
newParents.push(...Object.values(parents[i].$parents));
}
parents = newParents;
}
return undefined;
}
$has(prop: string): boolean {
return Object.prototype.hasOwnProperty.call(this, prop);
}
}
class A extends Class {
constructor() {
super({});
}
print(): string {
return 'Class A';
}
}
class B extends Class {
constructor() {
super({});
}
print(): string {
return 'Class B';
}
callOtherMethod(): string {
// This call would be equal to: this.print()
// It first checks if a child has the method, then checks for itself, then goes down
return this.$callUp('print', []);
}
}
class C extends Class {
constructor() {
// Pass parents of C
super({ A: new A(), B: new B() });
}
// override because of naming collision
print(): string {
// This call would be equal to: A.super.print()
// It only goes down the chain
return this.$parents.A.$callDown('print', []);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment