Background
How do you architect your code to allow circular function invocations across modules, where you basically have several mixins breaking down a class into several parts?
I have this essentially in the main index.ts file:
import Base from './base'
import treeMixin from './tree'
import forkMixin from './fork'
import nestMixin from './nest'
import knitMixin from './knit'
import textMixin from './text'
import cardDeckMixin from './card/deck'
import cardCodeMixin from './card/code'
Object.assign(Base.prototype, treeMixin)
Object.assign(Base.prototype, forkMixin)
Object.assign(Base.prototype, nestMixin)
Object.assign(Base.prototype, knitMixin)
Object.assign(Base.prototype, textMixin)
Object.assign(Base.prototype, cardCodeMixin)
Object.assign(Base.prototype, cardDeckMixin)
The Base
looks like this essentially:
export default class Base {}
The functionality of Base
is defined in these separate "mixins", where I have things like this:
nest.ts
export default {
mintNestTree(nest, seed) {
if (this.isTextNest(nest)) {
return this.getTextNest(nest, seed)
} else if (shared.isMark(nest)) {
} else if (shared.isSimpleTerm(nest)) {
}
},
// ...
}
text.ts
export default {
isTextNest(nest) {
if (nest.line.length > 1) {
return false;
}
if (nest.line.length === 0) {
return false;
}
let line = nest.line[0];
if (line.like === "text") {
return true;
}
return false;
},
// ...
};
More...
And it gets a lot more complex. Essentially I am building a complicated compiler, and parsing an AST. There is lots of recursion, yet I want to break these into separate files so it's not just one large 5k line file. Breaking it apart by theme essentially.
I use it like:
const base = new Base
base.isTextNest(node) // ...
Problem
I am getting this error in nest.ts
at isTextNest
:
Unsafe member access .isTextNest on an
any
value.this
is typed asany
.
You can try to fix this by turning on thenoImplicitThis
compiler option, or adding athis
parameter to the function. eslint@typescript-eslint/no-unsafe-member-access
.
How can I reorganize my code to make it suitable for TypeScript? (I am in the process of migrating a decently-sized JS project to TypeScript). Can I somehow add typing annotations to this
, or do I need to maybe stop using Object.assign(Base.prototype
and instead maybe do:
Object.assign(Base.prototype, Object.keys(mixin).reduce((m, x) => {
m[x] = (...params) => mixin[x].apply(null, [this].concat(params))
}, {})
Can it be done like that somehow, or in any less hacky way? If not, what is a standard way to reorganize my code?
I can do this:
mintNestTree(this: Base, nest: ParserNestNodeType, seed) {
if (this.isTextNest(nest)) {
return this.getTextNest(nest, seed)
} else if (shared.isMark(nest)) {
} else if (shared.isSimpleTerm(nest)) {
}
},
And use the this parameter, but it is still erroring because it seems like it's not catching that Object.assign
has mixed in other mixins as well.
Example
Here is a working simple example demonstrating my point more concisely.
class Base {}
const mixinA = {
fnA(this: Base) {
return this.fnB(1) + this.fnC(2)
}
}
const mixinB = {
fnB(x: number): number { return x * x }
}
const mixinC = {
fnC(this: Base, x: number): number { return this.fnB(x) / x }
}
Object.assign(Base.prototype, mixinA)
Object.assign(Base.prototype, mixinB)
Object.assign(Base.prototype, mixinC)