Show HN: Pg-typesafe – 为 PostgreSQL 和 TypeScript 提供强类型查询
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 环境变量(PGHOST、PGDATABASE 等)。
--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;
}