8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

用于在 drizzle 模式上执行基本 CRUD 操作的通用类

user26504905 2月前

24 0

当我在使用这些方法时包含关系时,无法从我的 findOne 和 findMany 方法的返回值中获得正确的类型推断。我已经创建了这个 BaseService 类,这样我就可以……

当我在使用这些方法时包含关系时,无法从我的 findOne 和 findMany 方法的返回值中获得正确的类型推断。

我创建了这个 BaseService 类,以便能够从其他服务中扩展它。这个通用类为我提供了通常需要的基本 CRUD 操作。

import { and, eq, TableRelationalConfig } from 'drizzle-orm';
import { PgTableWithColumns } from 'drizzle-orm/pg-core';
import { RelationalQueryBuilder } from 'drizzle-orm/pg-core/query-builders/query';
import { NotFoundError } from '@fullstack_package/core-application/errors';
import db from '../models/auth';

class BaseService<
  T extends PgTableWithColumns<{
    name: string;
    schema: undefined;
    columns: Record<string, any>;
    dialect: 'pg';
  }>,
  K extends RelationalQueryBuilder<T, TableRelationalConfig>
> {
  private entity: string;
  constructor(
    protected schema: T,
    protected tableName: K
  ) {
    //@ts-ignore
    this.entity = this.tableName.tableConfig.dbName;
  }

  public async createOne(data: T['$inferInsert']): Promise<T['$inferSelect']> {
    const [entity] = await db.insert(this.schema).values(data).returning();

    if (!entity)
      throw new NotFoundError(`${this.entity} returned as undefined`);

    return entity;
  }

  public async findMany(
    data: Partial<T['$inferSelect']> = {},
    extra?: Omit<NonNullable<Parameters<K['findFirst']>[0]>, 'where'>
  ): Promise<NonNullable<Awaited<ReturnType<K['findMany']>>>> {
    const keys = Object.keys(data) as Array<
      keyof Partial<typeof this.schema.$inferSelect>
    >;
    const values = Object.values(data) as Array<any>;
    const entity = await this.tableName.findMany({
      where: and(
        ...keys.map((key, index) => eq(this.schema[key], values[index]))
      ),
      ...extra
    });

    if (!entity.length) {
      throw new NotFoundError(`${this.entity} does not exist`);
    }

    return entity as NonNullable<Awaited<ReturnType<K['findMany']>>>;
  }

  public async findOne(
    data: Partial<T['$inferSelect']>,
    extra?: Omit<NonNullable<Parameters<K['findFirst']>[0]>, 'where'>
  ): Promise<NonNullable<Awaited<ReturnType<K['findFirst']>>>> {
    const keys = Object.keys(data) as Array<
      keyof Partial<typeof this.schema.$inferSelect>
    >;
    const values = Object.values(data) as Array<any>;

    const entity = await this.tableName.findFirst({
      where: and(
        ...keys.map((key, index) => eq(this.schema[key], values[index]))
      ),
      ...extra
    });

    if (!entity) {
      throw new NotFoundError(`${this.entity} does not exist`);
    }

    return entity as NonNullable<Awaited<ReturnType<K['findFirst']>>>;
  }

  public async updateOne(
    id: T['$inferSelect']['id'],
    data: Partial<T['$inferInsert']>
  ): Promise<T['$inferSelect']> {
    const [entity] = await db
      .update(this.schema)
      .set({ ...data })
      .where(eq(this.schema.id, id))
      .returning();

    if (!entity) throw new NotFoundError(`${this.entity} does not exits`);

    return entity;
  }

  public async deleteOneById(
    id: T['$inferSelect']['id']
  ): Promise<T['$inferSelect']> {
    const [entity] = await db
      .delete(this.schema)
      .where(eq(this.schema.id, id))
      .returning();

    if (!entity) throw new NotFoundError(`${this.entity} does not exits`);

    return entity;
  }
}

export default BaseService;

我像这样使用这个类

import db from '../models/auth';
import { UserSchema } from '../models/auth/schema';
import BaseService from './BaseService';

class UserService extends BaseService<
  typeof UserSchema,
  typeof db.query.UserSchema
> {
  constructor() {
    super(UserSchema, db.query.UserSchema);
  }
}

const USI = new UserService();

export default USI;

现在这个USI拥有所有基本的CRUD方法。

例子:

const user = await UserService.findOne({
    email: '[email protected]'
});

这里返回的用户类型是

const user: {
    first_name: string;
    last_name: string;
    email: string;
    password: string;
    tenant_id: string | null;
    createdAt: Date;
    updatedAt: Date | null;
    deletedAt: Date | null;
    approvedAt: Date | null;
    id: string;
    status: "deleted" | ... 2 more ... | "inactive";
    isReadonly: boolean;
}

到目前为止,一切都进展顺利。但是当我尝试在此或 findMany 方法中包含关系时,类型推断会返回该值的正确类型(在本例中为用户)。

例子:

const user = await UserService.findOne({
            email
        },{
        with: {
            tenant: true
        }
    }
);

这里用户类型应该有一个租户密钥,其类型为 tenenat 架构。但它没有显示它。结果是正确的,租户值在这个用户里面。但 TS 推断无法正常工作。

我已经尝试了所有我能尝试的方法。我甚至查看了开源 drizzle 代码,但它对我来说太复杂了。我需要与普通 drizzle 查询输出相同的类型推断。drizzle findFirst 方法示例

const user = await db.query.UserSchema.findFirst({
    where: eq(UserSchema.email, email),
    with: {
      tenant: true
    }
});

这里的用户类型

const user: {
    first_name: string;
    last_name: string;
    email: string;
    password: string;
    tenant_id: string | null;
    createdAt: Date;
    updatedAt: Date | null;
    deletedAt: Date | null;
    ... 4 more ...;
    tenant: {
        ...;
    } | null;
} | undefined

正如我们所见,它里面有租户密钥和相关数据。

帖子版权声明 1、本帖标题:用于在 drizzle 模式上执行基本 CRUD 操作的通用类
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由user26504905在本站《typescript》版块原创发布, 转载请注明出处!
最新回复 (0)
返回
作者最近主题: