EDIT: for more background, also see the discussion on ES Discuss.
I have three modules A
, B
, and C
. A
and <
I would recommend to use inversion of control. Make your C constructor pure by adding an A and a B parameter like this:
// --- Module A
import C from './C';
export default class A extends C {
// ...
}
// --- Module B
import C from './C'
export default class B extends C {
// ...
}
// --- Module C
export default class C {
constructor(A, B) {
// this may run later, after all three modules are evaluated, or
// possibly never.
console.log(A)
console.log(B)
}
}
// --- Entrypoint
import A from './A';
import B from './B';
import C from './C';
const c = new C(A, B);
console.log('Entrypoint', C, c);
document.getElementById('out').textContent = 'Entrypoint ' + C + ' ' + c;
https://www.webpackbin.com/bins/-KlDeP9Rb60MehsCMa8u
Update, in response to this comment: How to fix this ES6 module circular dependency?
Alternatively, if you do not want the library consumer to know about various implementations, you can either export another function/class that hides those details:
// Module ConcreteCImplementation
import A from './A';
import B from './B';
import C from './C';
export default function () { return new C(A, B); }
or use this pattern:
// --- Module A
import C, { registerA } from "./C";
export default class A extends C {
// ...
}
registerA(A);
// --- Module B
import C, { registerB } from "./C";
export default class B extends C {
// ...
}
registerB(B);
// --- Module C
let A, B;
const inheritors = [];
export const registerInheritor = inheritor => inheritors.push(inheritor);
export const registerA = inheritor => {
registerInheritor(inheritor);
A = inheritor;
};
export const registerB = inheritor => {
registerInheritor(inheritor);
B = inheritor;
};
export default class C {
constructor() {
// this may run later, after all three modules are evaluated, or
// possibly never.
console.log(A);
console.log(B);
console.log(inheritors);
}
}
// --- Entrypoint
import A from "./A";
import B from "./B";
import C from "./C";
const c = new C();
console.log("Entrypoint", C, c);
document.getElementById("out").textContent = "Entrypoint " + C + " " + c;
Update, in response to this comment: How to fix this ES6 module circular dependency?
To allow the end-user to import any subset of the classes, just make a lib.js file exporting the public facing api:
import A from "./A";
import B from "./B";
import C from "./C";
export { A, B, C };
or:
import A from "./A";
import B from "./B";
import C from "./ConcreteCImplementation";
export { A, B, C };
Then you can:
// --- Entrypoint
import { C } from "./lib";
const c = new C();
const output = ["Entrypoint", C, c];
console.log.apply(console, output);
document.getElementById("out").textContent = output.join();