Generic update component using table schema – how to type the table argument?

SOLVED IN BOTTOM COMMENT I'm attempting a generic update checkbox component where I can pass a type of MySQL (PlanetScale) table, and an associated keyName, and use those params in the db.update() query. Can anyone guide me on how to type the table argument?
import * as schema from '@/db/schema'
import { AnyMySqlTable } from 'drizzle-orm/mysql-core' // just found this, testing now
import { eq } from 'drizzle-orm'

export function CheckboxUpdate<T extends AnyMySqlTable>({ ...args }: {
model: T
keyName: keyof T & string
id: number
}) {
return (
<input
type='checkbox'
onChange={async (e) => {
'use server' // pseudo code
await db
.update(args.model)
.set({ [args.keyName]: e.target.checked })
.where(eq(args.model.id, args.id))
}}
/>
)
}

await CheckboxUpdate<typeof users>({
model: users,
keyName: 'isSubscriber',
id: 1,
})
import * as schema from '@/db/schema'
import { AnyMySqlTable } from 'drizzle-orm/mysql-core' // just found this, testing now
import { eq } from 'drizzle-orm'

export function CheckboxUpdate<T extends AnyMySqlTable>({ ...args }: {
model: T
keyName: keyof T & string
id: number
}) {
return (
<input
type='checkbox'
onChange={async (e) => {
'use server' // pseudo code
await db
.update(args.model)
.set({ [args.keyName]: e.target.checked })
.where(eq(args.model.id, args.id))
}}
/>
)
}

await CheckboxUpdate<typeof users>({
model: users,
keyName: 'isSubscriber',
id: 1,
})
1 Reply
mcgrealife
mcgrealifeOP2y ago
AnyMySqlTable didn't work as expected Removing the checkbox for simplicity. I'm getting a little closer like this:
export async function CheckboxUpdate<
T extends typeof schema.users | typeof schema.properties // <-- new
>({ ...args }: { model: T; keyName: keyof T; id: number }) {
await db
.update(args.model)
.set({ [args.keyName]: true }) // errors here
.where(eq(args.model.id, args.id))
}
export async function CheckboxUpdate<
T extends typeof schema.users | typeof schema.properties // <-- new
>({ ...args }: { model: T; keyName: keyof T; id: number }) {
await db
.update(args.model)
.set({ [args.keyName]: true }) // errors here
.where(eq(args.model.id, args.id))
}
Error:
Argument of type '{ [x: string]: any; }' is not assignable to parameter of type 'Simplify<{ [Key in keyof T["_"]["columns"]]?: SQL<unknown> | (T["_"]["columns"][Key]["_"]["notNull"] extends true ? T["_"]["columns"][Key]["_"]["data"] : T["_"]["columns"][Key]["_"]["data"] | null) | undefined; }, {}>'.ts(2345)
any
Argument of type '{ [x: string]: any; }' is not assignable to parameter of type 'Simplify<{ [Key in keyof T["_"]["columns"]]?: SQL<unknown> | (T["_"]["columns"][Key]["_"]["notNull"] extends true ? T["_"]["columns"][Key]["_"]["data"] : T["_"]["columns"][Key]["_"]["data"] | null) | undefined; }, {}>'.ts(2345)
any
If I change .update(args.model) to .update(schema.users) the .set() method does not error?
export async function CheckboxUpdate<
T extends typeof schema.users | typeof schema.properties
>({ ...args }: { model: T; keyName: keyof InferModel<T>; id: number }) {
await db
.update(schema.users) // changed from args.model to hardcode model
.set({ [args.keyName]: true }) // no error, but loses the generic model
.where(eq(args.model.id, args.id))
}
export async function CheckboxUpdate<
T extends typeof schema.users | typeof schema.properties
>({ ...args }: { model: T; keyName: keyof InferModel<T>; id: number }) {
await db
.update(schema.users) // changed from args.model to hardcode model
.set({ [args.keyName]: true }) // no error, but loses the generic model
.where(eq(args.model.id, args.id))
}
SOLVED: by inspecting the query builder methods, and typing the args to match This has the unexpected benefit of moving the complexity (and flexibility!) to the caller. For example, instead of defining only one keyName, the function now accepts "values", and can update any number of values!
import { SQL } from 'drizzle-orm'
import { AnyMySqlTable, MySqlUpdateSetSource } from 'drizzle-orm/mysql-core'

// function declaration
async function CheckboxUpdate<TTable extends AnyMySqlTable>({
...args
}: {
table: TTable
values: MySqlUpdateSetSource<TTable>
where: SQL
}) {
await db.update(args.table).set(args.values).where(args.where)
}

// implementation caller
await CheckboxUpdate<typeof users>({
table: users,
values: {
isSubscriber: true,
},
where: eq(users.id, 1),
})
import { SQL } from 'drizzle-orm'
import { AnyMySqlTable, MySqlUpdateSetSource } from 'drizzle-orm/mysql-core'

// function declaration
async function CheckboxUpdate<TTable extends AnyMySqlTable>({
...args
}: {
table: TTable
values: MySqlUpdateSetSource<TTable>
where: SQL
}) {
await db.update(args.table).set(args.values).where(args.where)
}

// implementation caller
await CheckboxUpdate<typeof users>({
table: users,
values: {
isSubscriber: true,
},
where: eq(users.id, 1),
})
Also thanks to @ngregrichardson https://discord.com/channels/1043890932593987624/1105287645363441704/1105287645363441704
Want results from more Discord servers?
Add your server