Typescript - 编译后获取未初始化的属性

时间:2018-02-21 13:40:03

标签: class typescript reflection socket.io

我目前正在编写socket.io的包装器。从面向对象的背景来看,我想在我的框架/包装器中实现模型的概念。

如果您碰巧知道socket.io,您可能知道您将与事件关联的数据作为参数,现在我已经实现了一个自定义路由系统,其中路由的处理程序获取快递中的数据。 js喜欢请求对象。

我们的想法是让模型类看起来像这样:

class XRequestModel
  @v.String({ message: 'The username must be a string!' })
  public userName: string;
}

路线事件可能如下所示:

@RouteConfig({ route: '/something', model: XRequestModel })
class XEvent extends Route {
  public on(req: Request<XRequestModel>, res: Response) {
    // Handle Event
  }
}

要完成此示例,请求对象的外观如下:

class Request<T> {
  public data: T;
}

现在,打字稿中的泛型非常有限,因为在编译后删除了类型信息,我不能使用通用的Request参数(这是模型的类型)从模型中获取元数据 - 在这种情况下,元数据是验证装饰者。为了解决这个问题,我将Model类的引用提供给RouteEvent的RouteConfig,它在内部使用,并允许我创建模型的实例,获取属性等等......

这里的想法是给路由的处理程序,一个带有预先验证的类型安全数据的请求对象。

阻止我从这里回来的事情是,未使用的属性,在通过typescript编译后被删除,所以我无法获取模型的元数据。初始化class-property可以解决这个问题:

class XRequestModel
  @v.String({ message: 'The username must be a string!' })
  public userName: string = '';
}

但我认为这会产生一些非常冗长的语法,我不想强​​迫这个包装器的用户初始化所有的模型属性。

实施方面的注意事项:

框架的用户必须将类注册到主要的&#39;从那里我可以通过装饰器反射得到Route-class。

当我尝试获取没有初始化属性的模型属性时 - 第一个模型示例。

// Here the route.config.model refers to the model from the RouteConfig
Object.getOwnPropertyNames(new route.config.model());
>>> []

以下是我使用初始化属性获得的内容:

Object.getOwnPropertyNames(new route.config.model());
>>> [ 'userName' ]

这是指向GitHub存储库的链接:https://github.com/FetzenRndy/SRocket 请注意,此回购中尚未实现模型。

基本上,我的问题是:如何在编译后获得具有未初始化属性的类的属性。

1 个答案:

答案 0 :(得分:3)

问题在于,如果没有发生初始化,则不会为字段发出代码,因此在运行时,对象上不存在该字段,直到为其分配值。

最简单的解决方案是初始化所有字段,即使只使用null也是如此:

class XRequestModel {
    public userName: string = null;
    public name: string = null;
}
var keys = Object.getOwnPropertyNames(new XRequestModel())
console.log(keys); // [ 'userName', 'name' ]

如果这对您来说不是一个可行的解决方案,您可以创建一个装饰器,添加到类上的静态字段,然后沿着原型链走到所有字段:

function Prop(): PropertyDecorator {
    return (target: Object, propertyKey: string): void => {
        let props: string[]
        if (target.hasOwnProperty("__props__")) {
            props = (target as any)["__props__"];
        } else {
            props = (target as any)["__props__"] = [];
        }
        props.push(propertyKey);
    };
}

class XRequestModelBase {
    @Prop()
    public baseName: string;
}

class XRequestModel extends XRequestModelBase {
    @Prop()
    public userName: string;
    @Prop()
    public name: string;
}
function getAllProps(cls: new (...args: any[]) => any) : string[] {
    let result: string[] = [];
    let prototype = cls.prototype;
    while(prototype != null) {
        let props: string[] = prototype["__props__"];
        if(props){
            result.push(...props);
        }
        prototype = Object.getPrototypeOf(prototype);
    }
    return  result;
}
var keys = getAllProps(XRequestModel);
console.log(keys);