Typescript 如何使用类型脚本中的反射获取实现某个基类的子类?
我们可以像C#一样在类型脚本中使用反射来获得实现某个基类的类列表吗 例如,假设Snake和Horse实现了基类Animal。现在我需要得到实现Animal的类。类似于我们在C#中所能做的: C#等效代码:Typescript 如何使用类型脚本中的反射获取实现某个基类的子类?,typescript,reflection,Typescript,Reflection,我们可以像C#一样在类型脚本中使用反射来获得实现某个基类的类列表吗 例如,假设Snake和Horse实现了基类Animal。现在我需要得到实现Animal的类。类似于我们在C#中所能做的: C#等效代码: var childTypes = assembly.GetTypes().Where(_ => _.IsSubclassOf(typeof(Animal))); class Animal { } class Snake extends Animal { } class Horse
var childTypes = assembly.GetTypes().Where(_ => _.IsSubclassOf(typeof(Animal)));
class Animal {
}
class Snake extends Animal {
}
class Horse extends Animal {
}
键入脚本类:
var childTypes = assembly.GetTypes().Where(_ => _.IsSubclassOf(typeof(Animal)));
class Animal {
}
class Snake extends Animal {
}
class Horse extends Animal {
}
一种方法是跟踪在一个全局数组中创建的所有类:
var allClasses = []
function register(c) {
allClasses.push(c);
}
register(class Animal {});
register(class Snake extends Animal {});
register(class Horse extends Animal {});
register(class Car {});
当您需要从特定类继承的类时,只需筛选出所需的类:
Object.values(allClasses).filter((c) => Animal.isPrototypeOf(c))
这个解决方案不是特定于Typescript的,它也可以与纯JavaScript一起工作。我需要对来自SOAP web服务的响应进行反序列化。我首先使用一个现有的SOAP客户机从返回的XML中获取一个对象,然后使用一个JSON反序列化器(来自)将这个对象反序列化为我想要的实际类型 问题是其中一个返回的属性被声明为基类型,并且实际上可能包含许多不同子类型的实例 我通过创建一个应用于派生类型的类装饰器来解决这个问题。这个类装饰器获取基类并对其应用元数据,描述派生类 要理解以下代码,重要的是要知道,如果来自web服务的XML包含表示多态性的
type
属性,那么XML->JSON解析器将添加一个$type
属性
应用于基类型的实际元数据是来自XML的类型名和派生类型的构造函数
元数据注册和类装饰器:
export interface IJsonPolymorphism {
name: string;
clazz: {new(): any};
}
export function RegisterForPolymorphism(name: string) {
return (target) => {
let metadata = getJsonPolymorphism(target.__proto__);
if (!metadata) {
metadata = [];
}
metadata.push({name: name, clazz: target});
target.__proto__ = Reflect.metadata(jsonPolymorphismMetadataKey, metadata)(target.__proto__);
return target;
};
}
export function getJsonPolymorphism(target: any): IJsonPolymorphism[] {
return Reflect.getMetadata(jsonPolymorphismMetadataKey, target);
}
用法:
// The base class
export class PropertyBase { /*...*/ }
// The derived classes
//
@RegisterForPolymorphism("b:PicklistProperty")
export class PicklistProperty extends PropertyBase { /*...*/ }
@RegisterForPolymorphism("b:TextProperty")
export class TextProperty extends PropertyBase { /*...*/ }
传递给类装饰器的字符串是来自web服务的XML响应中type
属性的值
反序列化程序的代码使用它的方式如下:
if (jsonObject["$type"]) {
const polymorphism = getJsonPolymorphism(clazz);
if (polymorphism && polymorphism.filter) {
const subclassDefinition = polymorphism.filter(x => x.name === jsonObject["$type"])[0];
if (subclassDefinition && subclassDefinition.clazz) {
clazz = subclassDefinition.clazz;
}
}
}
基本上,clazz
是将JSON对象反序列化到的类型的构造函数,我们将其替换为派生类型的构造函数
这段代码目前有一个限制,即它只将元数据添加到直接基类中,添加到整个层次结构中。但这可以通过调整
RegisterFormolymorphism
中的代码来轻松解决。如果您愿意依赖一个基于过时规范的可能很快改变的特性,并且如果您愿意只使用类而不使用接口,您可以使用装饰器来实现这一点
以下是一个例子:
跟踪层次结构。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);
}
}
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
没有必要求助于全球国家,它实际上相当整洁和明确
如果不使用TypeScript,这也适用于。(请注意,上述关于decorator用法的相同警告仍然适用)
当然,如果您不想依赖装饰器,那么手动编写此代码是很简单的:
import trackHierarchy from './hierarachy-tracked';
export class Animal { }
class Snake extends Animal { ... }
trackHierarchy(Snake);
export {Snake};
回到上面的示例代码,它很容易实现
它来自
var childTypes = assembly.GetTypes().Where(_ => _.IsSubclassOf(typeof(Animal)));
简单地
const childClasses = Animal.extendedBy || [];
一句警告
如果您发现自己想要编写这样的代码,您应该后退一步,确保您真正了解JavaScript。这种模式,以及您的示例用例,通常表明有人以经典的思维方式来到语言中,注意到了这些类,并开始认为它们与传统语言中的类相关
ES类不能不象C++、C++、java或Scala类。 首先也是最重要的:JavaScript中的类是而不是类型
JavaScript中的类是值它们的声明形式基本上只是原型之上的一种语法糖。你试图达到的模式表明你可能不太理解这一点。特别是,它表明您可能认为它们很特别。这是当前用于TypeScript的一个示例。你想解决的实际问题是什么?用TypeScript创建你的类怎么样?@PaulBullivant我喜欢这个工具,但我认为他在寻找不同的东西有什么原因我的答案不够充分而你没有接受吗?你能澄清一下可能遗漏了什么吗?这在巴别塔7中仍然有效,即使对装饰师进行了大量的更改。当然,如果你想安全起见,你只需要使用函数形式