If you are willing to depend on a function that may change soon, because it is based on an outdated specification, and if you are ready to use only classes, not interfaces, you can accomplish this using a decorator.
Here is an example:
<strong> Hierarchy-tracked.ts
export default function hierarchyTracked(target: new (...args: any[]) => object) { for (const proto of walkPrototypeChain(target)) { if (!Object.hasOwnProperty.call(proto, 'extendedBy')) { const extendedBy: typeof Function.extendedBy = []; Object.defineProperty(proto, 'extendedBy', { get: () => extendedBy }); } // ! is used to suppress a strictNullChecks error on optional. // This is OK since we know it is now defined. proto.extendedBy!.push(target); } } declare global { interface Function { // Declared as optional because not all classes are extended. extendedBy?: Array<new (...args: any[]) => object>; } } function* walkPrototypeChain(target: new (...args: any[]) => object) { let proto = Reflect.getPrototypeOf(target); while (proto && proto !== Object) { yield proto; proto = Reflect.getPrototypeOf(proto); } }
animals.ts
import hierarchyTracked from './hierarachy-tracked'; export class Animal { alive = true; static get slayable() {return true;} static extinct = false; } @hierarchyTracked export class Snake extends Animal { isEctotherm = true; } @hierarchyTracked export class Cobra extends Snake { isDeadly = true; } @hierarchyTracked export class Horse extends Animal { isEndotherm = true; }
There is no need to resort to a global state, and in fact it is quite neat and explicit.
This also works with Babel 7 if you are not using TypeScript. (note that the same caveats regarding using the decorators mentioned above still apply)
Of course, it is trivial to write manually if you do not want to rely on decorators:
import trackHierarchy from './hierarachy-tracked'; export class Animal { } class Snake extends Animal { ... } trackHierarchy(Snake); export {Snake};
Back to the above code example. It is easily achieved.
He goes from
var childTypes = assembly.GetTypes().Where(_ => _.IsSubclassOf(typeof(Animal)));
just
const childClasses = Animal.extendedBy || [];
Warning word
If you want to write such code, you need to take a step back and make sure that you really know JavaScript. This type of template and, indeed, your use case, usually indicates that someone came to the language with classical thinking, noticed ES 2015 classes and began to think that they are associated with classes in traditional languages.
ES classes cannot be less like C #, C ++, Java, or Scala classes.
First of all: classes in JavaScript are not types.
Classes in JavaScript are values .
Their declaration form is basically syntactic sugar over prototypes. The pattern you are trying to achieve assumes that you cannot understand it. In particular, this suggests that you might think that they are special.