网站搜索

当我通过装饰器实现抽象方法时,如何让 Typescript 识别我实际上已经实现了这些方法?


我有一个基本模型类,看起来像这样:

class Base {
    abstract public save(): Promise<Base>;
}

我的模型继承自 Base 并且必须实现 save 方法。例如,

class User extends Base {
    public save(): Promise<User> {
        // save my user in the database and return a promise like this...
        return getDatabaseConnection()
            .then((db) => db.insert(...))
            .then((res) => this);
    }
}

这工作得很好,并且 Typescript 对这里的类型没有任何问题。

但随着我的开发,我注意到我的模型 save 方法都非常相似...

class Group extends Base {
    public save(): Promise<Group> {
        // save my group in the database and return a promise like this...
        return getDatabaseConnection()
            .then((db) => db.insert(...))
            .then((res) => this);
    }
}

class OtherModel extends Base {
    public save(): Promise<OtherModel> {
        // save my other model in the database and return a promise like this...
        return getDatabaseConnection()
            .then((db) => db.insert(...))
            .then((res) => this);
    }
}

所以我想到,为了保持代码的简洁,我可以实现一个装饰器,将save方法添加到这些类中。

const save = (table: string, idColumn: string) => {
    return function decorator<T extends {new(...args: any[]): {}}>(target: T): T {
        return class extends target {
            public save = function() {
                // save whatever model this is & return it
                return getDatabaseConnection()
                    .then((db) => db.insert(...use the table, idColumn args...))
                    .then((res) => this);
            };
        }
    };
};

@save('user_table', 'id')
class User extends Base {}

@save('group_table', 'id')
class Group extends Base {}

@save('other_table', 'id')
class OtherModel extends Base {}

它运行得非常顺利,只是……Typescript 报告我的类声明中缺少抽象方法 save。

我一直在用 @ts-ignore 语句来绕过它,但我希望能移除它们。

我碰到这个问题是关于通过装饰器添加到类的新方法的,我明白装饰器的目的不是为了修改接口或契约,但这不是我在这里做的事情。我是想实现一个抽象方法,根据接口,它是存在的并且必须被实现。

当我通过装饰器实现抽象方法时,如何让 Typescript 识别我实际上已经实现了这些方法?

答案#1

我认为对你提出的问题的回答是,你应该在所有子类的 save() 方法上使用确定赋值断言,这告诉编译器该方法已经被实现,尽管它无法验证这一点。像这样:

@save('user_table', 'id')
class User extends Base {
    save!: () => Promise<this>;
}

@save('group_table', 'id')
class Group extends Base {
    save!: () => Promise<this>;
}

@save('other_table', 'id')
class OtherModel extends Base {
    save!: () => Promise<this>;
}

那会抑制错误,虽然有点重复,并且需要比我预计的你更愿意的更多手动标注。

这里的另一种方法可能是使用类工厂而不是装饰器。你可以让一个类继承任何求值为类构造函数的表达式。这和你使用装饰器的做法没有太大不同。首先我们创建工厂:

const Save = (table: string, idColumn: string) => class extends Base {
    public save() {
        // save whatever model this is & return it
        return Promise.resolve(this);
    }
};

上述 save() 方法的实现可以是你想要的任何内容,只要它适用于 Base。与装饰器的区别在于,对 Save 的调用会直接返回一个继承自 Base 的类,而不是返回一个扩展传入的类构造函数的函数。然后你的子类变为:

class User extends Save('user_table', 'id') {

}

class Group extends Save('group_table', 'id') {

}

class OtherModel extends Save('other_table', 'id') {

}

这些会被自动视为 Base 的正确子类,因此没有任何需要担心的错误。

好的,希望有帮助;祝你好运!

代码链接