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()
}
})
}