Exploring Methods
How can you structure your code to enable circular function calls across different modules, utilizing mixins to break down a class into various parts?
In my main index.ts file, I have the following setup:
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
class is defined as follows:
export default class Base {}
The core functionalities of Base
are implemented in separate "mixins", such as the ones shown below:
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;
},
// ...
};
Further Development
The system becomes more intricate with additional functions. It's similar to building a complex compiler and processing an AST structure. Despite numerous recursive operations, breaking them down into separate files helps maintain readability.
I make use of these methods like so:
const base = new Base
base.isTextNest(node) // ...
Challenges Faced
An error occurs in nest.ts
within the isTextNest
function:
Unsafe member access .isTextNest on an
any
value.this
is typed asany
.
To address this, consider enabling thenoImplicitThis
option or adding athis
parameter to the function. Use eslint.@typescript-eslint/no-unsafe-member-access
What changes can be made to align the code properly for TypeScript compatibility? (The project involves transitioning a sizable JavaScript codebase to TypeScript). Can typing annotations be incorporated for this
, or should there be a shift away from using Object.assign(Base.prototype
towards:
Object.assign(Base.prototype, Object.keys(mixin).reduce((m, x) => {
m[x] = (...params) => mixin[x].apply(null, [this].concat(params))
}, {})
Is it viable to proceed in this manner, or is there a smoother solution available? If not, what conventional approach would best restructure the existing code?
One potential resolution could involve:
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)) {
}
},
By utilizing the this parameter, although further adjustments may be needed due to the mixed-in nature of other mixins through Object.assign
.
https://i.sstatic.net/61Psd.png
Illustrative Scenario
A concise working example illustrating the key points mentioned above can be seen here.
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)