3

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

使用这个库,消费者将能够定义一个Person扩展Base类的模型类(例如),并且类装饰器将向 Person 类添加额外的数据(例如,instances数组,它将包含模型的所有实例,collection_name这将是模型的 MongoDB 集合名称等)。

TypeScript 游乐场中的代码。


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

4

1 回答 1

2

jcalz 指出的问题是您的装饰器正在接受一个已经具有静态属性Class的类型和. 您需要使用两个不同的接口,一个是简单地构造带有签名的实例的类型,另一个是扩展此接口以包含装饰器将添加的静态属性的类型。instancescollection_nameTnew(): 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';
    }
}

使用这种方法,所有Basestatic成员都必须是公开的。在装饰器中初始化的任何static成员都应该放在 中,而在装饰器中未初始化的任何剩余成员都应该放在 中。BaseDecoratedBaseConstructorstaticBaseConstructor

我假设您在实际代码中以某种方式在类中使用泛型类型TBase但如果您不这样做,您应该从Base类中删除泛型类型,其他所有内容仍然可以正常工作。

在这个操场上查看上面的代码片段。

于 2019-03-01T20:03:41.407 回答