TypeScript - 带有 Sequelize 的存储库模式
Posted
技术标签:
【中文标题】TypeScript - 带有 Sequelize 的存储库模式【英文标题】:TypeScript - Repository pattern with Sequelize 【发布时间】: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 会抱怨。
【问题讨论】:
【参考方案1】:我们遇到了同样的问题。解决方案是使用抽象存储库类实现的接口声明返回类型。
接口代码:
export type RepoResult<M> = Promise<Result<M | undefined, RepoError | undefined>>;
export interface IRepo<M>
save(model: M): RepoResult<M>;
findById(id: string): RepoResult<M>;
search(parameterName: string, parameterValue: string, sortBy: string, order: number, pageSize: number, pageNumber: number): RepoResult<M[]>;
getAll(): RepoResult<M[]>;
deleteById(id: string): RepoResult<M>;
findByIds(ids: string[]): RepoResult<M[]>;
deleteByIds(ids: string[]): RepoResult<any>;
;
抽象类的代码:
export abstract class Repo<M extends sequelize.Model> implements IRepo<M>
protected Model!: sequelize.ModelCtor<M>;
constructor(Model: sequelize.ModelCtor<M>)
this.Model = Model;
public async save(doc: M)
try
const savedDoc = await doc.save();
return Result.ok(savedDoc);
catch (ex: any)
logger.error(ex);
return Result.fail(new RepoError(ex.message, 500));
public async findById(id: string)
try
const doc = await this.Model.findOne(where:
id: id
);
if (!doc)
return Result.fail(new RepoError('Not found', 404));
return Result.ok(doc);
catch (ex: any)
return Result.fail(new RepoError(ex.message, 500));
希望对您有所帮助。祝你有美好的一天:)
编辑: 结果是一个如下所示的类:
export class Result<V, E>
public isSuccess: boolean;
public isFailure: boolean;
private error: E;
private value: V;
private constructor(isSuccess: boolean, value: V, error: E)
if (isSuccess && error)
throw new Error('Successful result must not contain an error');
else if (!isSuccess && value)
throw new Error('Unsuccessful error must not contain a value');
this.isSuccess = isSuccess;
this.isFailure = !isSuccess;
this.value = value;
this.error = error;
public static ok<V>(value: V): Result<V, undefined>
return new Result(true, value, undefined);
public static fail<E>(error: E): Result<undefined, E>
return new Result(false, undefined, error);
public getError(): E
if (this.isSuccess)
throw new Error('Successful result does not contain an error');
return this.error;
public getValue(): V
if (this.isFailure)
throw new Error('Unsuccessful result does not contain a value');
return this.value;
RepoError 类:
type RepoErrorCode = 404 | 500;
export class RepoError extends Error
public code: RepoErrorCode;
constructor(message: string, code: RepoErrorCode)
super(message);
this.code = code;
RepoResult 类型:
export type RepoResult<M> = Promise<Result<M | undefined, RepoError | undefined>>;
您可以在以下链接中找到有关该模式的更多信息: https://khalilstemmler.com/articles/enterprise-typescript-nodejs/functional-error-handling/
【讨论】:
这里的repoError和结果是什么?您能否在界面中添加这些导入? 完成!很高兴,如果它有帮助:)以上是关于TypeScript - 带有 Sequelize 的存储库模式的主要内容,如果未能解决你的问题,请参考以下文章
TypeScript - 带有 Sequelize 的存储库模式
sequelize-typescript - 当使用带有 `tsc` 编译器(带有 NestJS)的 glob 配置时,无法解析模型模块
Sequelize Typescript Sequelize Typescript TypeError:没有'new'就不能调用类构造函数模型
Sequelize-typescript 'HasManyCreateAssociationMixin' 不是函数