当我在使用这些方法时包含关系时,无法从我的 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
正如我们所见,它里面有租户密钥和相关数据。