当我通过装饰器实现抽象方法时,如何让 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 的正确子类,因此没有任何需要担心的错误。
好的,希望有帮助;祝你好运!
代码链接
