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

如何在TypeScript中为Kysely正确构建匹配类型键的数组?

Kysely 动态传入Select键的类型兼容方案

问题背景

我正在使用Vercel推荐的Kysely JS SQL构建器(文档/社区资源较少),它是全类型化的SQL构建器,通过Schema构建db对象后,查询时可识别表名和属性。

初始化代码如下:

import 'dotenv/config'
import { createKysely } from '@vercel/postgres-kysely'
import { DB } from 'kysely-codegen'

export const db = createKysely<DB>()
export { sql } from 'kysely'

DB接口由PostgreSQL Schema直接生成,示例片段:

export interface MyTable {
  id: string
  foo: string
  bar: boolean
}

export interface DB {
  my_table: MyTable
}

查询my_table时,静态传入键数组(如['foo', 'id'])可正常运行,但从JSON动态获取键时:

// 模拟未知输入,类似浏览器JSON API请求
const json = JSON.parse(fs.readFileSync('test.json'))
const selectKeys = Object.keys(json)

const record = await db
  .selectFrom('my_table')
  .select(selectKeys)
  .executeTakeFirst()

会触发类型错误:

Argument of type 'string[]' is not assignable to parameter of type 'SelectArg<DB, "my_table", SelectExpression<DB, "my_table">>'

目前通过类型断言或提前定义键数组过滤解决,但存在重复定义问题,需更优方案。


解决方案

方案1:类型守卫校验动态键

定义类型守卫函数,同时做运行时校验和类型推导,避免纯断言的不安全,且字段列表仅需维护一次:

import { MyTable } from './path-to-db-types'

type MyTableKey = keyof MyTable

function isMyTableKey(key: string): key is MyTableKey {
  return ['id', 'foo', 'bar'].includes(key)
}

// 使用时过滤并推导类型
const json = JSON.parse(fs.readFileSync('test.json'))
const selectKeys = Object.keys(json).filter(isMyTableKey)

const record = await db
  .selectFrom('my_table')
  .select(selectKeys)
  .executeTakeFirst()

方案2:利用Kysely的sql标记动态字段

适合字段来源可信的场景,直接将字段转为Kysely可识别的SQL表达式,同时避免SQL注入风险:

const json = JSON.parse(fs.readFileSync('test.json'))
const selectKeys = Object.keys(json)

const record = await db
  .selectFrom('my_table')
  .select(selectKeys.map(key => sql`${sql.raw(key)}`))
  .executeTakeFirst()

方案3:基于DB类型自动校验字段列表

用TypeScript的satisfies关键字,让运行时字段数组严格匹配类型定义,Schema变更时会自动提示更新:

import { DB } from './path-to-db-types'

type MyTableKey = keyof DB['my_table']
// TypeScript会校验数组元素是否符合MyTableKey类型
const MY_TABLE_KEYS = ['id', 'foo', 'bar'] as const satisfies readonly MyTableKey[]

// 过滤动态键
const json = JSON.parse(fs.readFileSync('test.json'))
const selectKeys = Object.keys(json).filter(key => MY_TABLE_KEYS.includes(key as MyTableKey)) as MyTableKey[]

const record = await db
  .selectFrom('my_table')
  .select(selectKeys)
  .executeTakeFirst()

内容的提问来源于stack exchange,提问作者Lance Pollard

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 18:05:15