Skip to content

Instantly share code, notes, and snippets.

@junlarsen
Last active November 11, 2019 15:45
Show Gist options
  • Save junlarsen/d02d3eed1cefd79916072a6522ef6ca9 to your computer and use it in GitHub Desktop.
Save junlarsen/d02d3eed1cefd79916072a6522ef6ca9 to your computer and use it in GitHub Desktop.
Implementation of multi inheritance output for a transpiler in JS
Save New Duplicate & Edit Just Text
// PoC for multiple class inheritance in JavaScript
// This is something your transpiler would emit
//
// If both super classes have a property then the baseclass
// must also have this property. Otherwise you will get ambiguous calls
// when calling BaseClass.BothParentsHaveThis()
interface Reference<T> {
thisRef: T
}
// Note: given the following scenario you must transpile to the super calls
// at compile time
//
// class A {
// method x() { print "Hi" }
// }
//
// class B extends A {}
//
// ; transpile this
// someB.x()
//
// Into this js code:
//
// someB.$parents.A.x()
interface Class<T> {
$parents: {
[_: string]: Class<T>
}
$instanceof(type: any): boolean
$constructor(ref: Reference<any>, ...args: any): void
}
// The following is the required boilerplate for a class
// You may omit the constructors if you want to
interface ReferenceA extends Reference<A> { }
class A implements Class<A> {
// List of superclasses
$parents = {}
// Need to do some compile time checks to translate alias imports
// otherwise stuff will probably break as type name is passed by
// string
$instanceof(type: any): boolean {
return type instanceof A
}
// Insert constructor body inside here, may add additional args with whatever
// the constructor will accept
$constructor(ref: ReferenceA /* , name: string */) {
}
// Should not be modifying the body of this constructor
// but you may add the additional args in here
constructor(/* name: string */) {
this.$constructor({
thisRef: this
// Any additional args must be passed in here
} /* , name */)
}
// Declare a method for this class taking a single string argument
example(name: string) {
// Wrapping the function makes everything easier, just dump the
// transpiled code into the lambda
// First arg is the references, second is our user defined arg
const caller = (ref: ReferenceA, name: string) => {
console.log(`Class A said hello with ${name}`)
}
return caller({
thisRef: this
}, name)
}
// Let's just declare a property on this class which we will mutate from class C
count = 0
}
// Just another copy
interface ReferenceB extends Reference<B> { }
class B implements Class<B> {
$parents = {}
$instanceof(type: any): boolean {
return type instanceof B
}
$constructor(ref: ReferenceB) {
}
constructor() {
this.$constructor({
thisRef: this
})
}
// Also implement example in B to make C require it
example(name: string) {
const caller = (ref: ReferenceB, name: string) => {
console.log(`Class B said hello with ${name}`)
}
return caller({
thisRef: this
}, name)
}
}
// A class which extends both of the above
interface ReferenceC extends Reference<C> {
refA: A
refB: B
}
// class C extends A, B
class C implements Class<C> {
$parents = { A: new A(), B: new B() }
$instanceof(type: any): boolean {
return type instanceof A
|| type instanceof B
|| type instanceof C
}
// Define a parameter age which is a number
$constructor(ref: ReferenceC, count: number) {
// Set super.A's count to this number
ref.refA.count = count
}
// Also define the argument here
constructor(count: number) {
this.$constructor({
thisRef: this,
refA: this.$parents.A,
refB: this.$parents.B
}, count)
}
// Both superclasses have .example, we have to implement it here as well.
example(name: string) {
const caller = (ref: ReferenceC, name: string) => {
console.log(`Class C said hello with ${name}`)
// Let's also call both of the parents just for the sake of it
ref.refA.example(name)
ref.refB.example(name)
// Time to change A.count
ref.refA.count += 100
}
return caller({
thisRef: this,
refA: this.$parents.A,
refB: this.$parents.B
}, name)
}
}
// Create instance, which will set A.count to 100
const example = new C(100)
// Print initial value ( 100 )
console.log(example.$parents.A.count)
// Call the function which mutates it
example.example("supergrecko")
// Will now be 200
console.log(example.$parents.A.count)
// Mutate it again
example.$parents.A.count = 300
// And log it again, 300
console.log(example.$parents.A.count)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment