【问题标题】:TypeScript - passing a class as an argument, and reflectionTypeScript - 将类作为参数传递,并进行反射
【发布时间】:2021-09-07 20:04:10
【问题描述】:

我正在编写一个通用的解组器。它将图形数据库数据转换为生成的 TypeScript (1.8.7) 模型类。输入是 JSON。输出应该是模型类的实例。
我的最终目标是创建类似 Hibernate OGM 的东西,仅用于 Tinkerpop Frames 和 TypeScript,中间有 REST 端点。

将类作为参数传递并访问它的静态成员的正确方法是什么?我想要这样的东西:

SomeModel some = <SomeModel> unmarshaller.fromJSON({/*Object from JSON*/}, SomeModel);

我试图写一个方法。 不确定我是否朝着正确的方向前进,请随时提出不同的方法。

public fromJSON(input: Object, clazz: typeof FrameModel): FrameModel
{
    // This only demonstrates access to Framemodel's metadata 
    // about original Java model classes.
    clazz.graphPropertyMapping;
    clazz.graphRelationMapping;

    let result = {};
    ...
    return result;
}
...

但是当我尝试在 Plunker 上执行此操作时,我遇到了执行错误,并显示了无用的堆栈跟踪。

模型超类如下所示:

/**
 * Things common to all Frames models on the Typescript side.
 */
export class FrameModel
{
    // Model metadata
    static discriminator: string;
    static graphPropertyMapping: { [key:string]:string; };
    static graphRelationMapping: { [key:string]:string; };

    // Each instance needs a vertex ID
    private vertexId: number;
    public getVertexId(): number {
        return this.vertexId;
    }
}

示例模型类:

import {TestPlanetModel} from './TestPlanetModel';
import {TestShipModel} from './TestShipModel';

export class TestGeneratorModel extends FrameModel
{
    static discriminator: string = 'TestGenerator';
    static graphPropertyMapping: { [key:string]:string; } = {
        bar: 'boo',
        name: 'name',
        rank: 'rank',
    };
    static graphRelationMapping: { [key:string]:string; } = {
        colonizes: 'colonizedPlanet',
        commands: 'ship',
    };

    boo: string;
    name: string;
    rank: string;

    public colonizedPlanet: TestPlanetModel[]; // edge label 'colonizedPlanet'

    public ship: TestShipModel; // edge label 'ship'

}

我没有找到太多关于 TypeScript 中反射和类处理的资料。
我知道如何在 Java 中做到这一点。
我知道如何在 JavaScript 中做到这一点。
我知道我可以使用装饰器获得类似的结果,但是对于生成的模型来说,使用字段或静态字段似乎更简单一些。

【问题讨论】:

  • 它的常规调用 class klass :)

标签: reflection typescript marshalling unmarshalling


【解决方案1】:

您可能已经注意到类成员不能有const 关键字。但是您可以改用static。如果您希望外部世界可以访问该成员,则该成员也应该是公开的。

public static graphPropertyMapping: { [key:string]:string; } = {
    bar: 'boo',
    name: 'name',
    rank: 'rank',
};

关于创建结果实例:

let result = new clazz();

//copy properties

return result;

【讨论】:

  • 对,上面的例子是手写的,所以包含这些错误。
  • 我实际上想知道 TypeScript 是如何实现和限制“静态”的——因为在 JavaScript 中,没有什么是真正静态的,它绑定到函数。所以从技术上讲,如果你从一个函数继承,我猜想在 TS 中类继承是如何完成的,你也继承了静态字段?因此MyClass.staticField 在技术上也可以作为MySubclass.staticField 访问?除非 TS 坚持 Java 的 static 概念...
  • 是的MyClass.staticField 也可以作为MySubclass.staticField 访问。你说得对,它绑定到函数(类定义)
【解决方案2】:

如果我对您的理解正确,那么以下内容可以帮助您入门:

interface Model {}

interface ModelData {}

interface MyModelConstructor<M extends Model, D extends ModelData> {
    new(data: D): M;
    // static members
    graphPropertyMapping: any;
    graphRelationMapping: any;
}

class Unmarshaller {
    public fromJSON<T>(input: string | ModelData, ctor: MyModelConstructor<T, ModelData>): T {
        let data: ModelData = (typeof input === "string") ? JSON.parse(input) : input;

        let propertyMapping = ctor.graphPropertyMapping;
        let relationMapping = ctor.graphRelationMapping;

        // do whatever with the mappings

        return new ctor(input);
    }
}

(code in playground)

我不知道你的模型是什么样子的,所以我希望这足够接近。

【讨论】:

  • 这看起来很明智。您能否添加一些链接来说明需要接口的基本原理?我的意思是,在 Java 中,不需要接口来检查类元数据和注释。这是一种“官方”方式还是利用了一些副作用?谢谢
  • 在 typescript interfaces have many roles 中,您不必使用它们,但这是有道理的。在我的示例中,我没有使用元数据或注释,这里唯一的就是表示对类的引用(而不是对实例的引用)的接口:MyModelConstructor。如果您更具体地了解您想知道的内容,我会修改我的答案以适应这一点。
  • 我更了解你的例子。看起来你建议为每个模型都有一个构造函数类。 IE。每个模型都有各自的构造函数类。由于我生成模型,我希望只有一个服务类能够根据 JSON 数据和模型类中的元数据构建模型。我想我会改进我的问题的代码。
  • 你不需要有不同的ctor类,你可以只有一个,这取决于你。此外,您不需要包含我包含的泛型部分,我认为这更容易,并且无需稍后再进行转换。但是由于您没有说明如何创建这些模型的实例,因此我很难给您一个更具体的答案。
  • 我还没有创建实例。这是我还没有弄清楚的事情之一。我最初的想法是创建一个Object,其中包含特定模型所需的相应属性,然后进行转换。
【解决方案3】:

我最近发布了 TypeScript 编译器的增强版本,它完全符合您的期望:从一个类中读取所有(静态或非静态)字段元数据。例如你可以写:

interface MyInterface {
    active:boolean;
    description: string;
}

class MyClass {
    id: number;
    name: string;
    myComplexField: MyInterface;
}

function printMembers(clazz: Class) {
    let fields = clazz.members.filter(m => m.type.kind !== 'function'); //exclude methods.
    for(let field of fields) {
        let typeName = field.type.kind;
        if(typeName === 'class' || typeName === 'interface') {
            typeName = (<Class | Interface>field.type).name;
        }
        console.log(`Field ${field.name} of ${clazz.name} has type: ${typeName}`);
    }
}

printMembers(MyClass.getClass());

这是输出:

$ node main.js
Field id of MyClass has type: number
Field name of MyClass has type: string
Field myComplexField of MyClass has type: MyInterface

当然,如果您将 clazz 的members 属性访问权限更改为statics,您将检索所有静态成员。这些信息也可以在编码时访问,因此您可以使用自动完成功能。 您可以对接口元数据执行相同的操作。例如,只需写MyInterface 并访问其成员。 你可以找到项目here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-03
    • 1970-01-01
    • 2020-05-03
    • 2022-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多