kysely-org / kysely

A type-safe typescript SQL query builder
https://kysely.dev
MIT License
10.8k stars 275 forks source link

JSON select types are working incorrectly with zod passthrough object #850

Closed vladshcherbin closed 10 months ago

vladshcherbin commented 10 months ago

Hey 👋

I have a JSON column and zod validated data is stored there. Same zod schema type is used in database JSONColumnType. Schema is a zod object type and everything works correctly.

When I add .passthrough() to object (passthrough is used for preserving unknown keys, specified ones always exist), Kysely loses the ability to get ref.key suggestions and result output type also has | null added.

Working Reproduction:

import { CamelCasePlugin, type GeneratedAlways, type JSONColumnType, Kysely, PostgresDialect } from 'kysely'
import pg from 'pg'
import { z } from 'zod'

const brandSchema = z.object({
  slug: z.string()
})

type Brand = z.infer<typeof brandSchema>
type SlugKey = Brand['slug']

interface BrandsTable {
  id: GeneratedAlways<number>
  details: JSONColumnType<Brand>
}

type Database = {
  brands: BrandsTable
}

const database = new Kysely<Database>({
  dialect: new PostgresDialect({
    pool: new pg.Pool({ connectionString: 'connection' })
  }),
  plugins: [
    new CamelCasePlugin()
  ]
})

const brand = await database
  .selectFrom('brands')
  .select((eb) => [
    'id',
    eb.ref('details', '->').key('slug').as('slug')
  ])
  .executeTakeFirstOrThrow()

console.log(brand.slug)

Result:

SlugKey has correct type

image

ref.key has correct type suggestions

image

✅ query result has correct type

image

Non-working:

Just one change to the above code:

const schema = object({
  slug: string()
}).passthrough()

Result

SlugKey still has correct type

image

ref.key has no type suggestions

image

❌ query result has incorrect type (slug always exists)

image

I've also tried using just ColumnType but the result is the same.

passthrough Brand object type:

image

kysely - 0.27.2 zod - 3.22.4 typescript - 5.3.3 node - 20.10.0 vscode - 1.85.2

koskimas commented 10 months ago

Interoperability with zod is not our goal.

vladshcherbin commented 10 months ago

@koskimas same thing happens with a plain object type, I just happened to found out with a zod one.

This will create same results:

type Brand = {
  slug: string
  [key: string]: unknown
}

I can create a new issue without zod if needed 😅

thelinuxlich commented 10 months ago

You should provide a reproduction with https://kyse.link/

vladshcherbin commented 10 months ago

@thelinuxlich thanks, will do 🙌

vladshcherbin commented 10 months ago

I've opened a new issue without zod with reproduction, kyse.link is insanely good 🤩