You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Drizzle-ORM:将查询构建器作为参数传递时应指定何种类型?

问题:Drizzle ORM通用分页函数中查询构建器的类型定义

我正在创建一个通用函数,用于为不同实体生成分页结果。为此,我将查询构建器的一部分用作子查询来获取记录数,并据此设置分页。

主函数代码

const findMany = async (options?: {
  sort?: AuthorSorting;
  filters?: AuthorFilter[];
  pagination?: QueryStringPagination;
}): Promise<{ data: Author[]; pagination: Pagination }> => {
  const query = db.select().from(authors);

  if (options?.sort) {
    const fieldName = options.sort.field;
    const sortingField = authors[fieldName] ?? authors["name"];

    query.orderBy(
      options.sort.direction === "desc" ? desc(sortingField) : asc(sortingField)
    );
  }

  if (options?.filters) {
    const conditions: SQL<Author>[] = [];
    const filters = options?.filters;

    filters.forEach((filter: Record<string, any>) => {
      const field = authors[filter.field as keyof Author];
      const op = sql.raw(Operators[filter.operator]);
      const value = filter.value;

      if (["like", "ilike"].includes(filter.operator)) {
        conditions.push(sql`${field} ${op} ${ "%" + value + "%" }`);
      } else {
        conditions.push(sql`${field} ${op} ${value}`);
      }
    });

    if (conditions.length > 0) {
      const combinedConditions = conditions.reduce(
        (prev, curr) => sql`${prev} AND ${curr}`
      );
      query.where(combinedConditions);
    }
  }

  return paginateQuery<Author>({
    query,
    QueryStringPagination: options.pagination,
  });
};

通用分页函数代码

import { sql } from "drizzle-orm";

import { db } from "../db";
import { Pagination, QueryStringPagination } from "../types";

export async function paginateQuery<T>({
  query,
  QueryStringPagination,
}: {
  query: any; // <---- 应设置何种类型?
  QueryStringPagination: QueryStringPagination;
}): Promise<{ data: T[]; pagination: Pagination }> {
  const subQuery = query.as("sub");
  const totalRecordsQuery = db
    .select({ total: sql<number>`count(*)` })
    .from(subQuery);

  const totalRecordsResult = await totalRecordsQuery.execute();
  const totalRecords = Number(totalRecordsResult[0].total);
  const totalPages = Math.ceil(totalRecords / QueryStringPagination.limit);

  query
    .limit(QueryStringPagination.limit)
    .offset((QueryStringPagination.page - 1) * QueryStringPagination.limit);

  const results = (await query.execute()) as T[];

  return {
    data: results,
    pagination: {
      totalRecords: totalRecords,
      totalPages: totalPages,
      currentPage: QueryStringPagination.page,
      limit: QueryStringPagination.limit,
    },
  };
}

Author实体代码

export const authors = pgTable(
  "authors",
  {
    id: uuid("id").defaultRandom().primaryKey(),
    name: varchar("name", { length: 256 }).notNull(),
    bio: text("bio").notNull(),
    birthdate: date("birthdate", { mode: "date" }).notNull(),
    createdAt: timestamp("createdAt").defaultNow().notNull(),
    updatedAt: timestamp("updatedAt").defaultNow().notNull(),
  },
  (authors) => {
    return {
      nameIndex: uniqueIndex("name_idx").on(authors.name),
    };
  }
);

我尝试了drizzle-orm中的不同类型,但都无法正确匹配。请问将查询构建器作为参数传递给函数时,应设置何种类型?


解决方案

在Drizzle ORM中,你需要使用**SelectQueryBuilder**类型(通用场景),或者对应数据库的具体查询构建器类型(比如PostgreSQL的PgSelectQueryBuilder)来替代any,既能获得类型安全,又能保持函数的通用性。

步骤1:导入正确的类型

从drizzle-orm核心包导入通用的SelectQueryBuilder,如果是特定数据库(如PostgreSQL),也可以从对应核心包导入更具体的类型:

import { sql, SelectQueryBuilder } from "drizzle-orm";
// 针对PostgreSQL的更具体类型(可选):
// import { PgSelectQueryBuilder } from "drizzle-orm/pg-core";

步骤2:修改分页函数的参数类型

将query的类型从any替换为SelectQueryBuilder,结合泛型适配不同实体类型:

export async function paginateQuery<T>({
  query,
  QueryStringPagination,
}: {
  query: SelectQueryBuilder<any, T>;
  QueryStringPagination: QueryStringPagination;
}): Promise<{ data: T[]; pagination: Pagination }> {
  const subQuery = query.as("sub");
  const totalRecordsQuery = db
    .select({ total: sql<number>`count(*)` })
    .from(subQuery);

  const totalRecordsResult = await totalRecordsQuery.execute();
  const totalRecords = Number(totalRecordsResult[0].total);
  const totalPages = Math.ceil(totalRecords / QueryStringPagination.limit);

  query
    .limit(QueryStringPagination.limit)
    .offset((QueryStringPagination.page - 1) * QueryStringPagination.limit);

  // 无需类型断言,TypeScript会自动推断结果类型为T[]
  const results = await query.execute();

  return {
    data: results,
    pagination: {
      totalRecords,
      totalPages,
      currentPage: QueryStringPagination.page,
      limit: QueryStringPagination.limit,
    },
  };
}

更严格的类型定义(可选)

如果需要区分表类型和查询结果类型(比如查询只返回部分字段时),可以使用双泛型:

export async function paginateQuery<TTable, TResult>({
  query,
  QueryStringPagination,
}: {
  query: SelectQueryBuilder<TTable, TResult>;
  QueryStringPagination: QueryStringPagination;
}): Promise<{ data: TResult[]; pagination: Pagination }> {
  // 函数逻辑保持不变
}

这样修改后,TypeScript能正确校验查询构建器的方法调用,同时保持函数对不同实体的通用性。


内容的提问来源于stack exchange,提问作者Cleber Gasparoto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 18:44:50