如何正确构建一个与TypeScript中的类型键匹配的键数组?

zlwx9yxi  于 2023-05-19  发布在  TypeScript
关注(0)|答案(1)|浏览(157)

我使用的是Kysely SQL builder for JS(根据Vercel的建议,尽管文档/社区很少)。它是一个完全类型化的SQL构建器。您使用模式构造了一个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模式生成,并存储在kysely-codegen node_modules文件夹中。它看起来像这样(简短的片段):

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

export interface DB {
  my_table: MyTable
}

现在,我的问题围绕着使用select函数,该函数接受MyTable的键数组(当查询my_table时)。

const record = await db
  .selectFrom('my_table')
  .select(['foo', 'id'])
  .executeTakeFirst()

那很好但是当我这样做的时候,它不起作用:

// simulate not knowing what the input is
// as if you were making a browser JSON API request
const json = JSON.parse(fs.readFileSync('test.json'))
const selectKeys = Object.keys(json)

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

我基本上得到了这个错误:
“string[]”类型的参数不能分配给“SelectArg<DB,“my_table”,SelectExpression<DB,“my_table”>>”类型的参数。
我可以这样修复它:

const record = await db
  .selectFrom('my_table')
  .select(selectKeys as Array<keyof MyTable>)
  .executeTakeFirst()

我还可以在代码前面的某个地方保证selectKeys是用my_table的键构建的,方法如下:

const MY_TABLE_KEYS: Array<keyof MyTable> = ['id', 'foo', 'bar']

function getKeys(json) {
  const keys = []
  for (const key in json) {
    if (MY_TABLE_KEYS.includes(key)) {
      keys.push(key)
    }
  }
  return keys
}

我基本上已经将数据库接口中的键复制到了一个数组中,但是getKeys函数有几个问题,不确定有什么方法可以解决。一个粗糙的TSPlayground在这里展示了我面临的一些挑战。
问题是,如何正确地键入要传递给.select方法的键?

tp5buhyn

tp5buhyn1#

您可能正在寻找类型 predicate /类型保护。类型 predicate 是Assert/声明参数是特定类型的函数,如果该函数返回true。在这种情况下,一个简单的 predicate 可能看起来像

(key: string): key is keyof MyTable => { return true; }

在上面的简单例子中,我总是返回true。为了使它有用,你不需要检查这个键是否是你所知道的键之一。您可以使用下面的示例来获取您正在寻找的内容。

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

const json: Partial<MyTable> = { id: "hello", foo: "world" };

// Get keys from json, then filter out all non-MyTable-keys
// our filter is a predicate
const keys = Object.keys(json).filter((key): key is keyof MyTable =>
    ["id", "foo", "bar"].includes(key),
);

const record: Partial<MyTable> = {};
keys.forEach((key) => (record[key] = json[key]));

您会注意到record[key] = json[key]仍然有一些问题,但这是因为json[key]可能未定义(因为它是一个部分);

相关问题