gpt4 book ai didi

node.js - TypeScript - 带有 Sequelize 的存储库模式

转载 作者:行者123 更新时间:2023-12-05 04:45:47 25 4
gpt4 key购买 nike

我正在将我的 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 不会提示诸如 findOnecreate 之类的方法不存在,但这会产生另一个问题。

现在,例如,每当我从存储库中获得 User 时,如果我尝试访问其属性之一,如 user.email ,TypeScript 将提示该属性不存在。当然,因为类型Model并不知道每个型号的具体情况。

好吧,那是 叛国罪 泛型。

现在 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 也(再次)不知道模型中的静态方法存在,例如 findAllcreate 等。

另一个问题是,在抽象类和具体类中,由于方法不再使用 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>

我研究了很多,试图让所有的方法都是静态的,但是静态方法不能引用泛型类型(因为它被认为是一个实例属性),尝试了一些变通方法,试图在 BaseRepository 的构造函数中传递具体模型以及使用通用类型的类,但到目前为止似乎没有任何效果。

如果您想检查代码: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 会提示。

最佳答案

我们遇到了同样的问题。解决方案是使用抽象存储库类实现的接口(interface)声明返回类型。

接口(interface)代码:

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;
}
}

repo 结果类型:

export type RepoResult<M> = Promise<Result<M | undefined, RepoError | undefined>>;

您可以在下面的链接中找到有关该模式的更多信息: https://khalilstemmler.com/articles/enterprise-typescript-nodejs/functional-error-handling/

关于node.js - TypeScript - 带有 Sequelize 的存储库模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69051499/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com