Show HN: Pg-typesafe – 为 PostgreSQL 和 TypeScript 提供强类型查询

发布: (2026年2月18日 GMT+8 02:15)
5 分钟阅读

Source: Hacker News

pg-typesafe

pg-typesafe 为 PostgreSQL 查询生成 TypeScript 类型。
没有运行时依赖,并且 不增加任何额外的冗余

const { rows } = client.query(
  "select id, name, last_modified from tbl where id = $1",
  [42],
);

查询的写法与普通的 pg 查询完全相同,但它是全类型化的:

  • 参数是必需的,且必须是 number 类型。
  • rows 的类型为 { id: number; name: string; last_modified: Date }[]

入门

安装 pg-typesafe

npm i -D pg-typesafe

首次运行:

npm exec pg-typesafe -- --connectionString postgres://postgres:example@localhost

这会生成 src/defs.gen.ts,其中包含 pg‑typesafe 类型。

将你的 Pool 强制转换为 pg‑typesafe 类型:

import type { TypesafePool } from "./defs.gen.ts";

export const pool = new Pool() as TypesafePool;

现在 pg‑typesafe 可以找到并为你的查询提供类型。每当你添加或修改查询时,重新生成类型:

npm exec pg-typesafe -- --connectionString postgres://postgres:example@localhost

Limitations

pg-typesafe 只能对 SQL 字符串为 常量 的查询进行类型推断。动态查询无法被分析,因此不会被类型化。使用常量查询还有助于防止 SQL 注入并提升性能。

命令行选项

--connectionString (default: undefined)

连接到 PostgreSQL 数据库的连接字符串。pg-typesafe 也会遵循标准的 node-pg 环境变量(PGHOSTPGDATABASE 等)。

--definitionsFile (default: src/defs.gen.ts)

生成的类型定义文件的路径。

--tsConfigFile (default: tsconfig.json)

项目的 tsconfig.json 的路径。pg-typesafe 使用此文件来定位要分析的源文件。

--configFile (default: pg-typesafe.config.ts)

pg‑typesafe 配置文件的路径。

配置文件

一个基本的 pg-typesafe.config.ts

import { defineConfig } from "pg-typesafe";

export default defineConfig({
  connectionString: "postgres://postgres:example@localhost",
});

完整的配置参数列表已通过 JSDoc 在该包中记录。

Recipes

将 BIGINT 转换为 JavaScript bigint

默认情况下,pg 会把 BIGINT 值作为字符串返回,因为它们可能超出 JavaScript 数字的安全整数范围。现代 Node.js 版本支持原生的 bigint 类型。

pg 端:

import { types } from "pg";

types.setTypeParser(20, (val) => BigInt(val));

在 pg‑typesafe 端: 配置转换器,使参数和结果字段使用 bigint

import {
  defineConfig,
  defaultTransformParameter,
  defaultTransformField,
} from "pg-typesafe";

export default defineConfig({
  // 参数转换
  transformParameter(param) {
    if (param.type_oid === 20) {
      return { type: "bigint" };
    }
    return defaultTransformParameter(param);
  },

  // 字段(结果列)转换
  transformField(field) {
    if (field.type_oid === 20) {
      return { type: "bigint" };
    }
    return defaultTransformField(field);
  },
});

将 JSONB 列类型映射为相应的 TypeScript 类型

如果你的 JSONB 列遵循已知的模式,可以将它们映射为特定的 TypeScript 接口。

import { defineConfig, defaultTransformField } from "pg-typesafe";

export default defineConfig({
  transformField(field) {
    if (field.type_oid === 3802 && field.column) {
      const c = field.column;
      const typeName = `${c.table_name}_${c.column_name}`;
      return {
        type: typeName,
        imports: [{ name: typeName, path: "./jsonb_columns.ts" }],
      };
    }
    return defaultTransformField(field);
  },
});

创建 jsonb_columns.ts 并在其中定义相应的接口,例如:

export interface hello_data {
  foo: string;
  bar: number;
}

类型传播

为了让生成的类型能够正确应用,请在整个代码中使用 pg‑typesafe‑enhanced 提供的类型。即使通过 pool.connect 获取客户端,这通常也会自动生效。

在传递连接时,你可以使用提供的类型对其进行注解:

import type {
  TypesafePoolClient,
  TypesafeQuerier,
  TypesafeQueryFn,
} from "./defs.gen.ts";

async function fetchFoos(client: TypesafeQuerier) {
  const { rows } = await client.query("select id, name from foo");
  return rows;
}

Alternatives

  • pgtyped – 为 .ts.sql 文件中的查询生成类型。对于 .ts 文件,它引入了自己的辅助函数,语法更冗长,而 pg-typesafe 则不添加额外语法。
  • kysely – 一个类型安全的查询构建器,接近 SQL,但在更高的抽象层次上工作。
0 浏览
Back to Blog

相关文章

阅读更多 »