kysely-org / kysely

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

[Question] best way to create reusable type-aware filters #978

Closed atreidesend closed 5 months ago

atreidesend commented 5 months ago

This might not be possible, or maybe I'm just struggling to figure out how best to approach it.

We have a usecase where we'd like to to facilitate a reusable time based filter, but allow it to be flexible as to what column it applies to, depending on the query you're constructing.

At the moment it looks something like this:

type GetReferenceExpression<T extends SelectQueryBuilder<any, any, any>> =
  T extends SelectQueryBuilder<infer DB, infer TB, any>
    ? ReferenceExpression<DB, TB>
    : never

export function applyMinimumDateFilter<
  T extends SelectQueryBuilder<any, any, any>,
>(query: T, column: GetReferenceExpression<T>, date: Date | string): T {
  return query.where(
    column,
    '>=',
    // this is where it breaks down, as `GetReferenceExpression<T>` hasn't
    // whittled down to just references that would produce date comparable
    // values, so we have to assert this to `<any>`
    sql<any>`date_trunc('second', ${
      typeof date === 'string' ? safeParseDateOrFail(date) : date
    }::timestamptz)`,
  ) as T
}

My first thought was to dive deeper into the conditional typings we were creating on our side, but I hit a wall and didn't really make much progress.

I've tried to search for a few variants of reusable, but not quite found the right match, apologies in advance if I've just missed it.

If you wanted to create a applyDateFilter(query, column/reference-expression, date) reusable utility, is there a recommended type-safe way to go about it?

koskimas commented 5 months ago

You can use the ExtractTypeFromReferenceExpression type. The following example doesn't directly solve the issue, but it only accepts values of correct type as the third argument:

https://kyse.link/uiKS-

If you want to filter out the column names that are not supported, you can traverse the reference expression type and filter out non-dates using the ExtractTypeFromReferenceExpression but that's probably an overkill and definitely unreadable for anyone else than the original writer.

koskimas commented 5 months ago

Here's a utility type for extracting column references of a specific type (like Date). But this type only extracts table-specified column references, which is only a small subset of what ReferenceExpression supports. But it might be all you need.

https://kyse.link/xxJLo