如何使用Type Script中的反射来获取实现某个基类的子类?

Bai*_*aig 5 reflection typescript

我们可以像C#一样在Type Script中使用反射来获取实现某个基类的类列表吗?

例如,假设Snake and Horse实现了基类Animal.现在我需要获得实现Animal的类.与我们在C#中可以做的类似:

C#等效代码:

var childTypes = assembly.GetTypes().Where(_ => _.IsSubclassOf(typeof(Animal)));
Run Code Online (Sandbox Code Playgroud)

键入脚本类:

class Animal {
}

class Snake extends Animal {
}

class Horse extends Animal {
}
Run Code Online (Sandbox Code Playgroud)

Alu*_*dad 5

如果你愿意依赖一个可能很快就会改变的特性,因为它基于一个过时的规范,如果你愿意只使用类而不使用接口,你可以使用装饰器来实现这一点。

下面是一个例子:

层次跟踪.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);
  }
}
Run Code Online (Sandbox Code Playgroud)

动物.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;
}
// logs
Animal.extendedBy && Animal.extendedBy.map(Taxon => Taxon.name)
  .forEach(name => {
    console.log(name);
  });
// Snake
// Cobra
// Horse

Snake.extendedBy && Snake.extendedBy.map(Taxon => Taxon.name)
  .forEach(name => {
    console.log(name);
  });
// Cobra
Run Code Online (Sandbox Code Playgroud)

没有必要求助于全局状态,它实际上非常整洁和明确。

如果您不使用 TypeScript,这也适用于Babel 7。(请注意,上面提到的关于装饰器使用的相同警告仍然适用)

当然,如果您不想依赖装饰器,那么手动编写是微不足道的:

import trackHierarchy from './hierarachy-tracked';

export class Animal { }

class Snake extends Animal { ... }
trackHierarchy(Snake);

export {Snake};
Run Code Online (Sandbox Code Playgroud)

回到上面的示例代码,很容易实现。

它来自

var childTypes = assembly.GetTypes().Where(_ => _.IsSubclassOf(typeof(Animal)));
Run Code Online (Sandbox Code Playgroud)

简单地

const childClasses = Animal.extendedBy || [];
Run Code Online (Sandbox Code Playgroud)

警告

如果您发现自己想要编写这样的代码,您应该退后一步,确保您确实了解 JavaScript。这种模式,实际上是您的示例用例,通常表明有人以经典的思维方式接触该语言,注意到 ES 2015 类,并开始认为它们与传统语言中的类有关。

ES 类与 C#、C++、Java 或 Scala 类非常相似。

首先也是最重要的:JavaScript 中的类不是类型。

JavaScript 中的类是

它们的声明形式基本上只是原型之上的语法糖。您试图实现的模式表明您可能不太了解这一点。特别是,它表明您可能认为它们很特别


Kam*_*zot 1

一种方法是跟踪您在一个全局数组中创建的所有类:

var allClasses = []
function register(c) {
  allClasses.push(c);
}
register(class Animal {});
register(class Snake extends Animal {});
register(class Horse extends Animal {});
register(class Car {});
Run Code Online (Sandbox Code Playgroud)

当您需要从特定类继承的类时,只需过滤掉您想要的类:

Object.values(allClasses).filter((c) => Animal.isPrototypeOf(c))
Run Code Online (Sandbox Code Playgroud)

该解决方案并非特定于 Typescript,它也适用于纯 JavaScript。