You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 14:47:39