TypeScript:使用类型化装饰器函数装饰派生类

时间:2019-03-01 19:16:54

标签: node.js typescript typescript-generics typescript-decorator typescript-class

我正在尝试使用TypeScript为node.js和MongoDB构建类似于“实体框架”的ORM库。

使用此库,使用者将能够定义将扩展Person类的Model类(例如Base),并且该类装饰器将向Person类添加其他数据(例如,instances数组将包含模型的所有实例,collection_name将是模型的MongoDB集合名称,等等。

TypeScript playground中的代码。


所以我的第一步是创建一个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_nameinstances属性:

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实例)
  • instancescollection_nameBase<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属性的类型?

1 个答案:

答案 0 :(得分:1)

问题as jcalz points out是您的装饰器正在接受类型为 具有静态属性Classinstances的{​​{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

相关问题