如何在Kysely中实现类型安全的exists方法?
Kysely BaseRepo exists方法类型错误解决
已实现BaseRepo类的create方法,但编写exists方法时遇到类型错误,无法为column参数找到正确类型,以下是相关代码及报错信息,寻求正确实现方式:
已实现的create方法代码
import { InsertObject, SelectType } from "kysely"; import { db } from "./dataSource"; import { DB } from "app/src/dbTypes" // 表类型 type TE = keyof DB & string // 可插入对象类型 type Insertable<T extends TE> = InsertObject<DB, T>; export class BaseRepo { static async create <T extends TE, I extends Insertable<T>> (item: I, table: T): Promise<SelectType<DB[T]["id"]> | undefined> { const result = await db() .insertInto(table) .values(item) .returning('id') .executeTakeFirst() return result?.id } }
尝试的exists方法代码
static async exists<T extends TE>(table: T, column: ReferenceExpression<DB, T>, value: any) { const result = await db() .selectFrom(table) .select(eb => eb.fn.countAll<number>().as('num')) .where(column, '=', value) .limit(1) .executeTakeFirstOrThrow() return result.num > 0 }
报错信息
Argument of type 'ReferenceExpression<DB, T>' is not assignable to parameter of type 'ReferenceExpression<DB, ExtractTableAlias<DB, T>>'. Type 'AnyColumnWithTable<DB, T>' is not assignable to type 'ReferenceExpression<DB, ExtractTableAlias<DB, T>>'. Type '`auth.users.${string}`' is not assignable to type 'ReferenceExpression<DB, ExtractTableAlias<DB, T>>'.ts(2345) (parameter) column: ReferenceExpression<DB, T>
解决方案
类型错误的核心是ReferenceExpression的泛型参数不匹配,selectFrom(table)后Kysely推断的表别名类型是ExtractTableAlias<DB, T>而非原始的T。你可以通过以下方式修复:
方式一:使用精确的列名类型
直接将column类型指定为对应表的列名,同时给value添加类型约束,提升类型安全性:
import { InsertObject, SelectType } from "kysely"; // ... 其他导入和类型定义不变 export class BaseRepo { // ... create方法不变 static async exists<T extends TE>( table: T, column: keyof DB[T] & string, value: DB[T][typeof column] ): Promise<boolean> { const result = await db() .selectFrom(table) .select(eb => eb.fn.countAll<number>().as('num')) .where(column, '=', value) .limit(1) .executeTakeFirstOrThrow() return result.num > 0 } }
方式二:匹配Kysely的ReferenceExpression正确类型
如果需要保留ReferenceExpression类型,调整泛型参数为ExtractTableAlias<DB, T>:
import { InsertObject, SelectType, ReferenceExpression, ExtractTableAlias } from "kysely"; // ... 其他导入和类型定义不变 export class BaseRepo { // ... create方法不变 static async exists<T extends TE>( table: T, column: ReferenceExpression<DB, ExtractTableAlias<DB, T>>, value: any ): Promise<boolean> { const result = await db() .selectFrom(table) .select(eb => eb.fn.countAll<number>().as('num')) .where(column, '=', value) .limit(1) .executeTakeFirstOrThrow() return result.num > 0 } }
额外优化:使用exists查询提升性能
相比count查询,直接使用Kysely的exists函数更高效,推荐写法:
static async exists<T extends TE>( table: T, column: keyof DB[T] & string, value: DB[T][typeof column] ): Promise<boolean> { const result = await db() .selectFrom(table) .where(column, '=', value) .select(eb => eb.fn.exists().as('exists')) .executeTakeFirst() return result?.exists === true }
内容的提问来源于stack exchange,提问作者Onur Eren Elibol
相关产品推荐
相关产品推荐

