Type mapping
How pg-describe-gen turns a PostgreSQL type name into a TypeScript type.
| PostgreSQL | TypeScript | |
|---|---|---|
smallint, integer, real, double precision | number | |
bigint | string | an int8 does not fit in a JS number; node-postgres returns a string |
numeric, decimal | string | arbitrary precision, and floats are wrong for money |
boolean | boolean | |
text, varchar, char, uuid, citext, inet, interval | string | |
date, timestamp, timestamptz | Date | |
time, time with time zone | string | no Date equivalent |
bytea | Buffer | |
json, jsonb | unknown | narrow it yourself |
T[] | T[] | any dimension |
| anything else | unknown | with a warning |
The mapping targets what node-postgres actually returns at run time, not
what the SQL type resembles. bigint and numeric arriving as strings is the
case that surprises people, and typing them number would be a lie the compiler
would then help you build on.
Overriding
Enums, domains and user-defined types fall through to unknown with a warning.
Map them, or override any built-in, with a types block in the config:
{
"queries": "queries",
"output": "src/generated/queries.ts",
"types": {
"order_status": "'pending' | 'shipped' | 'cancelled'",
"bigint": "bigint",
"jsonb": "Record<string, unknown>"
}
}
Keys are PostgreSQL type names as pg_describe reports them in type_name; the
quickest way to get the exact spelling is to run the query and look:
SELECT DISTINCT type_name FROM pg_describe($$SELECT * FROM orders$$);
Values are emitted verbatim into the generated file, so any TypeScript type
expression works — a union, an imported type name, a branded type. Nothing
validates it beyond tsc on the generated output.
If you map bigint to the JS bigint, remember to configure node-postgres to
parse int8 accordingly; the generator describes types, it does not install
parsers.