【发布时间】:2021-11-02 04:24:13
【问题描述】:
我正在将我的 Express API 模板转换为 TypeScript,但我在存储库中遇到了一些问题。
使用 JavaScript,我会做这样的事情:
export default class BaseRepository {
async all() {
return this.model.findAll();
}
// other common methods
}
import BaseRepository from './BaseRepository';
import { User } from '../Models';
export default class UserRepository extends BaseRepository {
constructor() {
super();
this.model = User;
}
async findByEmail(email) {
return this.model.findOne({
where: {
email,
},
});
}
// other methods
现在,使用 TypeScript,问题在于它不知道 this.model 的类型,我无法将具体模型传递给 BaseRepository,因为它是一个抽象。我发现sequelize-typescript 导出了一个ModelCtor,它声明了所有静态模型方法,如findAll、create 等,我还可以使用另一个sequelize-typescript 导出Model 来正确注释返回类型。
所以,我最终这样做了:
import { Model, ModelCtor } from 'sequelize-typescript';
export default abstract class BaseRepository {
protected model: ModelCtor;
constructor(model: ModelCtor) {
this.model = model;
}
public async all(): Promise<Model[]> {
return this.model.findAll();
}
// other common methods
}
import { Model } from 'sequelize-typescript';
import BaseRepository from './BaseRepository';
import { User } from '../Models';
export default class UserRepository extends BaseRepository {
constructor() {
super(User);
}
public async findByEmail(email: string): Promise<Model | null> {
return this.model.findOne({
where: {
email,
},
});
}
// other methods
}
好的,这行得通,TypeScript 不会抱怨诸如 findOne 或 create 之类的方法不存在,但这会产生另一个问题。
现在,例如,每当我从存储库中获得 User 时,如果我尝试访问其属性之一,例如 user.email,TypeScript 就会抱怨该属性不存在。当然,因为Model这个类型不知道每个型号的具体情况。
好吧,那就是 treason 泛型。
现在 BaseRepository 使用通用的 Model 类型,方法也使用该类型:
export default abstract class BaseRepository<Model> {
public async all(): Promise<Model[]> {
return Model.findAll();
}
// other common methods
}
具体类将适当的模型传递给泛型类型:
import BaseRepository from './BaseRepository';
import { User } from '../Models';
export default class UserRepository extends BaseRepository<User> {
public async findByEmail(email: string): Promise<User | null> {
return User.findOne({
where: {
email,
},
});
}
// other methods
}
现在 IntelliSense 可以正确亮起,它同时显示抽象类和具体类方法以及模型属性(例如 user.email)。
但是,正如您所想象的那样,这会导致更多问题。
在BaseRepository 内部,方法使用Model 泛型类型,TypeScript 抱怨'Model' only refers to a type, but is being used as a value here。不仅如此,TypeScript 也(再次)不知道模型中的静态方法存在,例如 findAll、create 等。
另一个问题是,在抽象类和具体类中,由于方法不再使用this,ESLint 期望方法是静态的:Expected 'this' to be used by class async method 'all'。好的,我可以在整个文件中忽略此规则,错误就消失了。将所有方法设置为静态会更好,因此我不必实例化存储库,但也许我做梦太多了。
值得一提的是,虽然我可以使用 // @ts-ignore 来消除这些错误,但当我执行此操作时,它不起作用:TypeError: Cannot read property 'create' of undefined\n at UserRepository.<anonymous>
我研究了很多,试图让所有方法都是静态的,但是静态方法不能引用泛型类型(因为它被认为是实例属性),尝试了一些变通方法,尝试在@的构造函数中传递具体模型987654352@ 以及使用泛型类型的类,但到目前为止似乎没有任何效果。
如果您想检查代码:https://github.com/andresilva-cc/express-api-template/tree/main/src/App/Repositories
编辑:
找到这个:Sequelize-Typescript typeof model
好的,我从那篇帖子中删除了一些不必要的代码,这有点用:
import { Model } from 'sequelize-typescript';
export default abstract class BaseRepository<M extends Model> {
constructor(protected model: typeof Model) {}
public async all(attributes?: string[]): Promise<M[]> {
// Type 'Model<{}, {}>[]' is not assignable to type 'M[]'.
// Type 'Model<{}, {}>' is not assignable to type 'M'.
// 'Model<{}, {}>' is assignable to the constraint of type 'M', but 'M' could be instantiated with a different subtype of constraint 'Model<any, any>'.
return this.model.findAll({
attributes,
});
}
import BaseRepository from './BaseRepository';
import { User } from '../Models';
export default class UserRepository extends BaseRepository<User> {
constructor() {
super(User);
}
}
我的意思是,如果我输入一些 // @ts-ignore,它至少会执行,并且 IntelliSense 会完美亮起,但 TypeScript 会抱怨。
【问题讨论】:
标签: node.js typescript sequelize.js typescript-generics sequelize-typescript