Help with proper types (values)

I've been working to try to get a dynamic method to related/unrelate records together on a join table
async linkEntities<TJoinTable extends Table>(
joinTable: TJoinTable,
sourceIdField: keyof TJoinTable,
relatedEntityField: keyof TJoinTable,
sourceId: string,
relatedEntityIds: string[],
) {
// 1. Fetch existing related entity IDs

const column = joinTable[sourceIdField] as Column // required to cast to satisfy where/eq

type ExpectedRowType = {
// maps over keys in the join table to be string or null
[K in keyof TJoinTable]: string | null
}

const existingRelatedEntities = (await this.db
.select()
.from(joinTable)
.where(eq(column, sourceId))) as ExpectedRowType[] // to get existingIds to work

const existingIds = existingRelatedEntities.map(
row => row[relatedEntityField],
)
// 2. Diff to get to-be-deleted and to-be-added IDs
const toBeDeleted = existingIds.filter(
id => id !== null && !relatedEntityIds.includes(id), // check if null should be included??
)

const toBeAdded = relatedEntityIds.filter(
id => !existingIds.includes(id),
)

// 3. Delete old relations

await this.db.transaction(async tx => {
if (toBeDeleted.length) {
...
}
if (toBeAdded.length) {
const payload: Record<string, string>[] = toBeAdded.map(id => ({
[sourceIdField]: sourceId,
[relatedEntityField]: id,
}))
const insertedRecords = await tx
.insert(joinTable)
.values(payload) // TODO: proper type?
.returning()

}
})
}
async linkEntities<TJoinTable extends Table>(
joinTable: TJoinTable,
sourceIdField: keyof TJoinTable,
relatedEntityField: keyof TJoinTable,
sourceId: string,
relatedEntityIds: string[],
) {
// 1. Fetch existing related entity IDs

const column = joinTable[sourceIdField] as Column // required to cast to satisfy where/eq

type ExpectedRowType = {
// maps over keys in the join table to be string or null
[K in keyof TJoinTable]: string | null
}

const existingRelatedEntities = (await this.db
.select()
.from(joinTable)
.where(eq(column, sourceId))) as ExpectedRowType[] // to get existingIds to work

const existingIds = existingRelatedEntities.map(
row => row[relatedEntityField],
)
// 2. Diff to get to-be-deleted and to-be-added IDs
const toBeDeleted = existingIds.filter(
id => id !== null && !relatedEntityIds.includes(id), // check if null should be included??
)

const toBeAdded = relatedEntityIds.filter(
id => !existingIds.includes(id),
)

// 3. Delete old relations

await this.db.transaction(async tx => {
if (toBeDeleted.length) {
...
}
if (toBeAdded.length) {
const payload: Record<string, string>[] = toBeAdded.map(id => ({
[sourceIdField]: sourceId,
[relatedEntityField]: id,
}))
const insertedRecords = await tx
.insert(joinTable)
.values(payload) // TODO: proper type?
.returning()

}
})
}
No description
1 Reply
DiamondDragon
DiamondDragonOP15mo ago
Have an error on payload at the end . But generally just having a tough time getting the types to play nicely Overload 1 of 2, '(value: { [Key in keyof { [K in keyof ({ [Key in keyof TJoinTable["_"] This is example usage of the method
const queryHelper = new QueryHelper(db)
await db.execute(sql`SET session_replication_role = replica`)
// Mock ActivitiesToContacts
const activityId = uuidv7()
const values = [uuidv7(), uuidv7(), uuidv7()]
const payload = [
{ activityId: activityId, contactId: values[0] },
{ activityId: activityId, contactId: values[1] },
{ activityId: activityId, contactId: values[2] },
]

await db.insert(activitiesToContacts).values(payload)
// Mock removing all existing values except one and inserting new ones,
const newValues = [uuidv7(), uuidv7(), values[1], uuidv7(), uuidv7()]

const res = await queryHelper.linkEntities(
activitiesToContacts,
'activityId',
'contactId',
activityId,
newValues,
)
const queryHelper = new QueryHelper(db)
await db.execute(sql`SET session_replication_role = replica`)
// Mock ActivitiesToContacts
const activityId = uuidv7()
const values = [uuidv7(), uuidv7(), uuidv7()]
const payload = [
{ activityId: activityId, contactId: values[0] },
{ activityId: activityId, contactId: values[1] },
{ activityId: activityId, contactId: values[2] },
]

await db.insert(activitiesToContacts).values(payload)
// Mock removing all existing values except one and inserting new ones,
const newValues = [uuidv7(), uuidv7(), values[1], uuidv7(), uuidv7()]

const res = await queryHelper.linkEntities(
activitiesToContacts,
'activityId',
'contactId',
activityId,
newValues,
)
Want results from more Discord servers?
Add your server