kysely-org / kysely

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

Excessively Deep Typescript Error When Passing SelectQueryBuilder to function #1273

Open ppetro08 opened 1 day ago

ppetro08 commented 1 day ago

I am trying to build a function to handle pagination on a SelectQueryBuilder that is passed to it, but I am getting the typescript error Type instantiation is excessively deep and possibly infinite. ts(2589)

I tried the kysely-paginate package but it results in the same error.

My project is on typescript v5.5.4.

Does anyone know any typescript type magic to resolve the error?

Here is the function in question.

export const executeWithPagination = async <
  DB,
  O,
  TB extends keyof DB,
>(
  queryBuilder: SelectQueryBuilder<DB, TB, O>,
  opts: PaginationOpts
) => {
  queryBuilder = queryBuilder
    .offset((opts.pageNumber - 1) * opts.pageSize)
    .fetch(opts.pageSize + 1);

  const rows = await queryBuilder.execute();
  const hasPrevPage = rows.length > 0 ? opts.pageNumber > 1 : false;
  const hasNextPage = rows.length > 0 ? rows.length > opts.pageSize : false;

  if (hasNextPage) {
    rows.pop();
  }

  return {
    rows,
    pageInfo: {
      hasPrevPage,
      hasNextPage,
    },
  };
};
igalklebanov commented 1 day ago

Hey 👋

Separate concerns, simplify generics:

function paginate(pageNumber: number, pageSize: number) {
  return <QB extends SelectQueryBuilder<any, any, any>>(qb: QB): QB => 
    qb.offset((pageNumber - 1) * pageSize)
      .fetch(pageSize + 1)
}

function processPaginatedResults<R>(results: R[], pageNumber: number, pageSize: number): {
  page: R[],
  hasNext: boolean,
  hasPrev: boolean,
} {
  const hasNext = results.length > pageSize;
  const hasPrev = pageNumber > 1 && rows.length > 0;

  return {
    page: hasNext ? page.slice(0, -1) : page,
    hasNext,
    hasPrev,
  }
}

const pageNumber = 2;
const pageSize = 100;

const results = await db.selectFrom("table")
  .selectAll()
  .$call(paginate(pageNumber, pageSize))
  .execute()

const { page, hasNext, hasPrev } = processPaginatedResults(results, pageNumber, pageSize)