Typing a helper function parameter to be a table with required column(s)

I'm having a hard time typing some helper functions where I need some params to accept any table as long as it implements certain required columns (key and type). Here's what i'm aiming for:
export function withTranslation<T extends AnyTable, TT extends AnyTable</* How to type this to enforce certain columns, e.g. "locale"? */>>(
event: RequestEvent | ServerLoadEvent,
table: T,
translationsTable: TT,
join: { field: ValueOf<T['_']['columns']>; reference: ValueOf<TT['_']['columns']> }
) {
return dbpool.$with('tt').as((db) =>
db
.select({
...getTableColumns(table),
...getTableColumns(translationsTable),
})
.from(table)
.leftJoin(
translationsTable,
and(
eq(translationsTable.locale, event.locals.locale),
// ^^^^^^Property "locale" does not exist on type TT
eq(join.field, join.reference))
)
);
}
export function withTranslation<T extends AnyTable, TT extends AnyTable</* How to type this to enforce certain columns, e.g. "locale"? */>>(
event: RequestEvent | ServerLoadEvent,
table: T,
translationsTable: TT,
join: { field: ValueOf<T['_']['columns']>; reference: ValueOf<TT['_']['columns']> }
) {
return dbpool.$with('tt').as((db) =>
db
.select({
...getTableColumns(table),
...getTableColumns(translationsTable),
})
.from(table)
.leftJoin(
translationsTable,
and(
eq(translationsTable.locale, event.locals.locale),
// ^^^^^^Property "locale" does not exist on type TT
eq(join.field, join.reference))
)
);
}
Anyone have experience with this?
7 Replies
Angelelz
Angelelz11mo ago
How about:
TT extends AnyTable & { locale: any }
TT extends AnyTable & { locale: any }
iolyd
iolyd11mo ago
Hmmm, that could very well do! For some reason I was trying to use AnyTable 's generic instead of just doing an intersection
Angelelz
Angelelz11mo ago
I mean, you could, but why complicate it:
TT extends AnyTable<{ name: "", columns: {locale: any}, dialect: "your dialect" }>
TT extends AnyTable<{ name: "", columns: {locale: any}, dialect: "your dialect" }>
iolyd
iolyd11mo ago
And say I want to be stricter regarding locale's type since I know it should be implemented on tables using a common definition:
/* Could implement more columns... */
export const baseColumns = {
locale: locale('locale')
.references(() => locales.locale, {
onDelete: 'cascade',
onUpdate: 'cascade',
})
.notNull(),
//...
};
/* Could implement more columns... */
export const baseColumns = {
locale: locale('locale')
.references(() => locales.locale, {
onDelete: 'cascade',
onUpdate: 'cascade',
})
.notNull(),
//...
};
I'm struggling to infer a table-agnostic column type for this.
TT extends AnyTable & { [K in keyof typeof baseColumns]: AnyColumn & (typeof baseColumns)[K]> }
TT extends AnyTable & { [K in keyof typeof baseColumns]: AnyColumn & (typeof baseColumns)[K]> }
(sorry for the deluge of things named locale)
Angelelz
Angelelz11mo ago
The columns that you pass to a table definition get transformed by the table function What you could do is use one of the tables where those columns are implemented and pull the types from them:
type BaseColumns = Pick<typeof MyTable, "locale" | "anotherColumn" | "anotherOne">
type BaseColumns = Pick<typeof MyTable, "locale" | "anotherColumn" | "anotherOne">
iolyd
iolyd11mo ago
Alrighty, here's what I've got then:
export function withTranslation<
T extends AnyTable,
TT extends AnyTable & { [K in keyof TranslationBaseColumns]: AnyColumn },
J extends { field: ValueOf<T['_']['columns']>; reference: ValueOf<TT['_']['columns']> },
>(
event: RequestEvent | ServerLoadEvent,
table: T,
translationsTable: TT,
join: J | ((table: T, translationsTable: TT) => J)
) {
const { field, reference } = 'field' in join ? join : join(table, translationsTable);
return dbpool
.select({
...getTableColumns(table),
...getTableColumns(translationsTable),
})
.from(table)
.leftJoin(
translationsTable,
and(eq(translationsTable.locale, event.locals.locale), eq(field, reference))
);
}
export function withTranslation<
T extends AnyTable,
TT extends AnyTable & { [K in keyof TranslationBaseColumns]: AnyColumn },
J extends { field: ValueOf<T['_']['columns']>; reference: ValueOf<TT['_']['columns']> },
>(
event: RequestEvent | ServerLoadEvent,
table: T,
translationsTable: TT,
join: J | ((table: T, translationsTable: TT) => J)
) {
const { field, reference } = 'field' in join ? join : join(table, translationsTable);
return dbpool
.select({
...getTableColumns(table),
...getTableColumns(translationsTable),
})
.from(table)
.leftJoin(
translationsTable,
and(eq(translationsTable.locale, event.locals.locale), eq(field, reference))
);
}
Ah, yeah I could try that
Angelelz
Angelelz11mo ago
Then you could do:
TT extends AnyTable & BaseColumns
TT extends AnyTable & BaseColumns
Want results from more Discord servers?
Add your server