我正在尝试使用TypeScript为node.js和MongoDB构建类似于“实体框架”的ORM库。
使用此库,使用者将能够定义将扩展Person
类的Model类(例如Base
),并且该类装饰器将向Person类添加其他数据(例如,instances
数组将包含模型的所有实例,collection_name
将是模型的MongoDB集合名称,等等。
所以我的第一步是创建一个Base
类:
class Base<T>
{
static collection_name: string
static instances: Base<any>[]
_id: string
}
因此用户将可以像这样定义自己的模型:
@decorator<Person>({collection_name: 'people'})
class Person extends Base<Person>
{
@field name
@field address
...
}
然后,我创建了一个装饰器类来设置Person类的collection_name
和instances
属性:
function decorator<T>(config: { collection_name: string }) {
return function <T extends Base<T>>(Class: IClass<T>) {
Class.collection_name = config.collection_name;
Class.instances = [];
return Class
}
}
装饰器函数接收用户生成的Class
,而我正在尝试创建一个描述此类的类型的接口。我称它为IClass
:
interface IClass<T>
{
new(): Base<T>
instances: Base<T>[];
collection_name: string
}
new
是构造函数(返回一个Base
实例)instances
和collection_name
是Base<T>
的静态属性,此处是非静态的(我不确定这一点,对吗?)但是,当尝试定义用户模型时,出现以下错误:
@decorator<Person>({collection_name: 'people'}) // <==== ERROR HERE ===
class Person extends Base<Person>
{
}
错误:(23,2)TS2345:类型'typeof Person'的参数不是 可分配给“ IClass>”类型的参数。
“ typeof Person”类型缺少属性“ instances”,但 类型“ typeof Person”缺少类型的以下属性 在“ IClass>”类型中是必需的。
当检查Base
的类型时,打字稿编译器似乎忽略了从typeof Person
继承的静态成员。
如何定义装饰器函数的Class属性的类型?
答案 0 :(得分:1)
问题as jcalz points out是您的装饰器正在接受类型为 具有静态属性Class
和instances
的{{1}}。您需要使用两种不同的接口,一种是使用collection_name
签名简单地构造T
实例的类型,另一种是将该接口扩展为包括装饰器将添加的静态属性的类型。>
new(): T
采用这种方法,class Base<T> {
static _id = 0;
private _id: number;
constructor () {
this._id = Base._id++;
}
}
interface BaseConstructor<T extends Base<T>> {
_id: number;
new(): T;
}
interface DecoratedBaseConstructor<T extends Base<T>> extends BaseConstructor<T> {
instances: T[];
collection_name: string;
}
function decorator<T extends Base<T>>(config: { collection_name: string }) {
return (baseConstructor: BaseConstructor<T>): DecoratedBaseConstructor<T> => {
const decoratedBaseConstructor = baseConstructor as Partial<DecoratedBaseConstructor<T>>;
decoratedBaseConstructor.collection_name = config.collection_name;
decoratedBaseConstructor.instances = [];
return decoratedBaseConstructor as DecoratedBaseConstructor<T>;
};
}
@decorator<Person>({collection_name: 'people'})
class Person extends Base<Person> {
name: string;
constructor () {
super();
this.name = 'foo';
}
}
的所有Base
成员必须是公开的。在装饰器中初始化的static
的所有static
成员都应进入Base
,而在装饰器中未初始化的所有其余DecoratedBaseConstructor
成员都应进入static
代替。
我假设您在实际代码中以某种方式在BaseConstructor
类中使用了通用类型T
,但是如果没有,则应该从Base
中删除通用类型课,其他所有内容仍然一样。
查看上面的代码段in this playground。