类 - Typescript - 编译后获取未初始化的属性
我目前正在为 socket.io 编写一个封装。由于我来自非常面向对象的背景,我想在我的框架/封装中实现模型的概念。
如果你恰好了解 socket.io,你可能知道你会将与事件关联的数据作为参数获取,现在我已经实现了一个自定义路由系统,其中路由的处理程序会在一个类似 express.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;
}
现在 TypeScript 中的泛型非常有限,因为类型信息在编译后会被移除,我无法使用泛型 Request 参数(即模型的类型)来获取模型的元数据——在这种情况下,元数据是验证装饰器。为了解决这个问题,我将模型类的引用提供给 RouteEvent 的 RouteConfig,这在内部被使用,并允许我创建模型实例、获取属性等等……
这里的想法是给路由的处理器一个具有预先验证的、类型安全数据的请求对象。
阻碍我做这件事的原因是,未使用的属性在 TypeScript 编译后会被移除,所以我无法获取模型的元数据。初始化类属性可以解决这个问题:
class XRequestModel
@v.String({ message: 'The username must be a string!' })
public userName: string = '';
}
但我认为这会导致一些非常冗长的语法,而且我不想强迫这个封装的用户初始化所有模型属性。
实现附注:
框架的使用者必须将类注册到一个“主”类中,然后我可以通过装饰器反射获取 Route 类。
当我尝试获取未初始化属性的模型的属性时 - 第一个模型示例。
// 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
问题是,如果没有进行初始化,就不会为字段生成任何代码,因此在运行时,该字段在对象上不存在,直到给它赋值为止。
最简单的解决方法是初始化所有字段,即使你只是用 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);
