1
0
mirror of https://github.com/theoludwig/kysely-typegen.git synced 2026-05-22 16:23:25 +02:00

feat: add KyselyTypegenPostgresDialect

This commit is contained in:
2026-05-20 20:42:16 +02:00
parent d9839d01c7
commit 3072a6bf43
3 changed files with 1032 additions and 11 deletions
@@ -0,0 +1,608 @@
exports[`typegen > generate types matching snapshot 1`] = `
{
"lines": [
"// This file was automatically generated by \`kysely-typegen\`.",
"// Do not edit this file manually.",
"",
"import type { ColumnType } from \\"kysely\\"",
"",
"export type Generated<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>",
"",
"export type Timestamp = ColumnType<Date, Date | string, Date | string>",
"",
"export type Numeric = ColumnType<string, number | string, number | string>",
"",
"export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>",
"",
"export type Json = JsonValue",
"",
"export type JsonArray = JsonValue[]",
"",
"export interface JsonObject {",
" [x: string]: JsonValue | undefined",
"}",
"",
"export type JsonPrimitive = boolean | number | string | null",
"",
"export type JsonValue = JsonArray | JsonObject | JsonPrimitive",
"",
"export type Currency = \\"EUR\\" | \\"GBP\\" | \\"USD\\"",
"",
"export type OrderStatus = \\"cancelled\\" | \\"paid\\" | \\"pending\\" | \\"shipped\\"",
"",
"export type UserRole = \\"admin\\" | \\"guest\\" | \\"member\\"",
"",
"export interface AllTypes {",
" colBit: string",
" colBool: boolean",
" colBoolDefault: Generated<boolean>",
" colBoolNullable: boolean | null",
" colBox: string",
" colBpchar: string",
" colBytea: Buffer",
" colCidr: string",
" colDate: Timestamp",
" colFloat4: number",
" colFloat8: number",
" colInet: string",
" colInt2: number",
" colInt4: number",
" colInt8: Int8",
" colJson: Json",
" colJsonb: Json",
" colJsonbDefault: Generated<Json>",
" colLine: string",
" colLseg: string",
" colMacaddr: string",
" colMoney: string",
" colNumeric: Numeric",
" colOid: number",
" colPath: string",
" colPoint: unknown",
" colPolygon: string",
" colText: string",
" colTextNullable: string | null",
" colTime: string",
" colTimestamp: Timestamp",
" colTimestampDefault: Generated<Timestamp>",
" colTimestamptz: Timestamp",
" colTimetz: string",
" colTsquery: string",
" colTsvector: string",
" colUuid: string",
" colVarbit: string",
" colVarchar: string",
" colXml: string",
" createdAt: Generated<Timestamp>",
" id: Generated<string>",
" updatedAt: Timestamp | null",
"}",
"",
"export interface Orders {",
" amountCents: number",
" createdAt: Generated<Timestamp>",
" currency: Generated<Currency>",
" id: Generated<Int8>",
" note: string | null",
" status: Generated<OrderStatus>",
" userId: string",
"}",
"",
"export interface Users {",
" createdAt: Generated<Timestamp>",
" email: string | null",
" id: Generated<string>",
" isActive: Generated<boolean>",
" role: Generated<UserRole>",
" username: string",
"}",
"",
"export interface DB {",
" AllTypes: AllTypes",
" Orders: Orders",
" Users: Users",
"}"
],
"enums": [
{
"name": "Currency",
"values": [
"EUR",
"GBP",
"USD"
]
},
{
"name": "OrderStatus",
"values": [
"cancelled",
"paid",
"pending",
"shipped"
]
},
{
"name": "UserRole",
"values": [
"admin",
"guest",
"member"
]
}
],
"tables": [
{
"columns": [
{
"dataType": "uuid",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "id"
},
{
"dataType": "bool",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colBool"
},
{
"dataType": "bool",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": true,
"name": "colBoolNullable"
},
{
"dataType": "bool",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colBoolDefault"
},
{
"dataType": "int2",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colInt2"
},
{
"dataType": "int4",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colInt4"
},
{
"dataType": "int8",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colInt8"
},
{
"dataType": "float4",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colFloat4"
},
{
"dataType": "float8",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colFloat8"
},
{
"dataType": "numeric",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colNumeric"
},
{
"dataType": "money",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colMoney"
},
{
"dataType": "text",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colText"
},
{
"dataType": "varchar",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colVarchar"
},
{
"dataType": "bpchar",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colBpchar"
},
{
"dataType": "bytea",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colBytea"
},
{
"dataType": "date",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colDate"
},
{
"dataType": "time",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colTime"
},
{
"dataType": "timetz",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colTimetz"
},
{
"dataType": "timestamp",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colTimestamp"
},
{
"dataType": "timestamptz",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colTimestamptz"
},
{
"dataType": "json",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colJson"
},
{
"dataType": "jsonb",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colJsonb"
},
{
"dataType": "uuid",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colUuid"
},
{
"dataType": "inet",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colInet"
},
{
"dataType": "cidr",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colCidr"
},
{
"dataType": "macaddr",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colMacaddr"
},
{
"dataType": "bit",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colBit"
},
{
"dataType": "varbit",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colVarbit"
},
{
"dataType": "xml",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colXml"
},
{
"dataType": "tsvector",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colTsvector"
},
{
"dataType": "tsquery",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colTsquery"
},
{
"dataType": "point",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colPoint"
},
{
"dataType": "line",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colLine"
},
{
"dataType": "lseg",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colLseg"
},
{
"dataType": "box",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colBox"
},
{
"dataType": "path",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colPath"
},
{
"dataType": "polygon",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colPolygon"
},
{
"dataType": "oid",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colOid"
},
{
"dataType": "text",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": true,
"name": "colTextNullable"
},
{
"dataType": "jsonb",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colJsonbDefault"
},
{
"dataType": "timestamp",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "colTimestampDefault"
},
{
"dataType": "timestamptz",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "createdAt"
},
{
"dataType": "timestamptz",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": true,
"name": "updatedAt"
}
],
"isForeign": false,
"isView": false,
"name": "AllTypes",
"schema": "public"
},
{
"columns": [
{
"dataType": "int8",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": true,
"isAutoIncrementing": true,
"isNullable": false,
"name": "id"
},
{
"dataType": "uuid",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "userId"
},
{
"dataType": "OrderStatus",
"dataTypeSchema": "public",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "status"
},
{
"dataType": "Currency",
"dataTypeSchema": "public",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "currency"
},
{
"dataType": "int4",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "amountCents"
},
{
"dataType": "text",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": true,
"name": "note"
},
{
"dataType": "timestamptz",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "createdAt"
}
],
"isForeign": false,
"isView": false,
"name": "Orders",
"schema": "public"
},
{
"columns": [
{
"dataType": "uuid",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "id"
},
{
"dataType": "varchar",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": false,
"name": "username"
},
{
"dataType": "text",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": false,
"isAutoIncrementing": false,
"isNullable": true,
"name": "email"
},
{
"dataType": "UserRole",
"dataTypeSchema": "public",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "role"
},
{
"dataType": "bool",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "isActive"
},
{
"dataType": "timestamptz",
"dataTypeSchema": "pg_catalog",
"hasDefaultValue": true,
"isAutoIncrementing": false,
"isNullable": false,
"name": "createdAt"
}
],
"isForeign": false,
"isView": false,
"name": "Users",
"schema": "public"
}
]
}
`;
+234 -9
View File
@@ -1,17 +1,242 @@
import assert from "node:assert/strict"
import { describe, it } from "node:test"
import { fn } from "../index.ts"
import type { StartedPostgreSqlContainer } from "@testcontainers/postgresql"
import { PostgreSqlContainer } from "@testcontainers/postgresql"
import { Kysely, sql } from "kysely"
import { PostgresJSDialect } from "kysely-postgres-js"
import path from "node:path"
import { after, before, describe, it, snapshot } from "node:test"
import postgres from "postgres"
import { KyselyTypegenPostgresDialect } from "../index.ts"
describe("index", () => {
it('should return "Hello, tsdown!"', () => {
snapshot.setResolveSnapshotPath((testFilePath) => {
if (testFilePath == null) {
throw new Error('"testFilePath" is null.')
}
const dir = path.dirname(testFilePath)
const base = path.basename(testFilePath)
return path.join(dir, "__snapshots__", `${base}.snapshot`)
})
const POSTGRES_IMAGE =
"docker.io/postgres:18.4@sha256:f7ce845ee6873dd84be93c9828fe0d1fab0f9707dc9ac569694657398b290bce"
const createSchema = async (database: Kysely<any>): Promise<void> => {
await database.schema.createType("Currency").asEnum(["EUR", "USD", "GBP"]).execute()
await database.schema.createType("UserRole").asEnum(["admin", "member", "guest"]).execute()
await database.schema
.createType("OrderStatus")
.asEnum(["pending", "paid", "shipped", "cancelled"])
.execute()
await database.schema
.createTable("AllTypes")
.addColumn("id", "uuid", (column) => {
return column
.notNull()
.primaryKey()
.defaultTo(sql`gen_random_uuid()`)
})
.addColumn("colBool", "boolean", (column) => {
return column.notNull()
})
.addColumn("colBoolNullable", "boolean")
.addColumn("colBoolDefault", "boolean", (column) => {
return column.notNull().defaultTo(false)
})
.addColumn("colInt2", "smallint", (column) => {
return column.notNull()
})
.addColumn("colInt4", "integer", (column) => {
return column.notNull()
})
.addColumn("colInt8", "bigint", (column) => {
return column.notNull()
})
.addColumn("colFloat4", "real", (column) => {
return column.notNull()
})
.addColumn("colFloat8", "double precision", (column) => {
return column.notNull()
})
.addColumn("colNumeric", sql`numeric(12, 2)`, (column) => {
return column.notNull()
})
.addColumn("colMoney", sql`money`, (column) => {
return column.notNull()
})
.addColumn("colText", "text", (column) => {
return column.notNull()
})
.addColumn("colVarchar", "varchar(255)", (column) => {
return column.notNull()
})
.addColumn("colBpchar", sql`char(10)`, (column) => {
return column.notNull()
})
.addColumn("colBytea", "bytea", (column) => {
return column.notNull()
})
.addColumn("colDate", "date", (column) => {
return column.notNull()
})
.addColumn("colTime", "time", (column) => {
return column.notNull()
})
.addColumn("colTimetz", sql`timetz`, (column) => {
return column.notNull()
})
.addColumn("colTimestamp", "timestamp", (column) => {
return column.notNull()
})
.addColumn("colTimestamptz", "timestamptz", (column) => {
return column.notNull()
})
.addColumn("colJson", "json", (column) => {
return column.notNull()
})
.addColumn("colJsonb", "jsonb", (column) => {
return column.notNull()
})
.addColumn("colUuid", "uuid", (column) => {
return column.notNull()
})
.addColumn("colInet", sql`inet`, (column) => {
return column.notNull()
})
.addColumn("colCidr", sql`cidr`, (column) => {
return column.notNull()
})
.addColumn("colMacaddr", sql`macaddr`, (column) => {
return column.notNull()
})
.addColumn("colBit", sql`bit(8)`, (column) => {
return column.notNull()
})
.addColumn("colVarbit", sql`varbit(16)`, (column) => {
return column.notNull()
})
.addColumn("colXml", sql`xml`, (column) => {
return column.notNull()
})
.addColumn("colTsvector", sql`tsvector`, (column) => {
return column.notNull()
})
.addColumn("colTsquery", sql`tsquery`, (column) => {
return column.notNull()
})
.addColumn("colPoint", sql`point`, (column) => {
return column.notNull()
})
.addColumn("colLine", sql`line`, (column) => {
return column.notNull()
})
.addColumn("colLseg", sql`lseg`, (column) => {
return column.notNull()
})
.addColumn("colBox", sql`box`, (column) => {
return column.notNull()
})
.addColumn("colPath", sql`path`, (column) => {
return column.notNull()
})
.addColumn("colPolygon", sql`polygon`, (column) => {
return column.notNull()
})
.addColumn("colOid", sql`oid`, (column) => {
return column.notNull()
})
.addColumn("colTextNullable", "text")
.addColumn("colJsonbDefault", "jsonb", (column) => {
return column.notNull().defaultTo(sql`'{}'::jsonb`)
})
.addColumn("colTimestampDefault", "timestamp", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.addColumn("createdAt", "timestamptz", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.addColumn("updatedAt", "timestamptz")
.execute()
await database.schema
.createTable("Users")
.addColumn("id", "uuid", (column) => {
return column
.notNull()
.primaryKey()
.defaultTo(sql`gen_random_uuid()`)
})
.addColumn("username", "varchar(50)", (column) => {
return column.notNull().unique()
})
.addColumn("email", "text")
.addColumn("role", sql`"UserRole"`, (column) => {
return column.notNull().defaultTo("member")
})
.addColumn("isActive", "boolean", (column) => {
return column.notNull().defaultTo(true)
})
.addColumn("createdAt", "timestamptz", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.execute()
await database.schema
.createTable("Orders")
.addColumn("id", "bigserial", (column) => {
return column.notNull().primaryKey()
})
.addColumn("userId", "uuid", (column) => {
return column.notNull().references("Users.id")
})
.addColumn("status", sql`"OrderStatus"`, (column) => {
return column.notNull().defaultTo("pending")
})
.addColumn("currency", sql`"Currency"`, (column) => {
return column.notNull().defaultTo("EUR")
})
.addColumn("amountCents", "integer", (column) => {
return column.notNull()
})
.addColumn("note", "text")
.addColumn("createdAt", "timestamptz", (column) => {
return column.notNull().defaultTo(sql`now()`)
})
.execute()
}
describe("typegen", () => {
let container: StartedPostgreSqlContainer
let database: Kysely<any>
before(async () => {
container = await new PostgreSqlContainer(POSTGRES_IMAGE).start()
database = new Kysely<any>({
dialect: new PostgresJSDialect({
postgres: postgres({
database: container.getDatabase(),
host: container.getHost(),
port: container.getPort(),
user: container.getUsername(),
password: container.getPassword(),
}),
}),
})
await createSchema(database)
})
after(async () => {
await database.destroy()
await container.stop()
})
it("generate types matching snapshot", async (testContext) => {
// Arrange - Given
const input = "tsdown"
const databaseTypegen = new KyselyTypegenPostgresDialect({ database })
// Act - When
const output = fn(input)
const result = await databaseTypegen.typegen()
// Assert - Then
const expected = "Hello, tsdown!"
assert.strictEqual(output, expected)
testContext.assert.snapshot(result)
})
})
+190 -2
View File
@@ -1,3 +1,191 @@
export const fn = (input: string): string => {
return `Hello, ${input}!`
import type { Kysely, TableMetadata as KyselyTableMetadata } from "kysely"
export type TableMetadata = KyselyTableMetadata
export interface EnumMetadata {
name: string
values: string[]
}
export abstract class KyselyTypegenDialect {
public abstract readonly database: Kysely<any>
public abstract readonly scalars: Record<string, string>
public async getTables(): Promise<TableMetadata[]> {
const tables = await this.database.introspection.getTables()
return tables.sort((a, b) => {
return a.name.localeCompare(b.name)
})
}
public getTablesTypegen(tables: TableMetadata[], enums: EnumMetadata[]): string[] {
const enumNames = new Set(
enums.map((enumMetadata) => {
return enumMetadata.name
}),
)
const result: string[] = []
for (const table of tables) {
const interfaceName = table.name
result.push(`export interface ${interfaceName} {`)
const columns = [...table.columns].sort((a, b) => {
return a.name.localeCompare(b.name)
})
for (const column of columns) {
const isEnum = enumNames.has(column.dataType)
const baseType = isEnum ? column.dataType : (this.scalars[column.dataType] ?? "unknown")
let columnType = column.isNullable ? `${baseType} | null` : baseType
if (column.hasDefaultValue || column.isAutoIncrementing) {
columnType = `Generated<${columnType}>`
}
result.push(` ${column.name}: ${columnType}`)
}
result.push("}", "")
}
return result
}
protected abstract getEnumsMap(): Promise<Map<string, string[]>>
public async getEnums(): Promise<EnumMetadata[]> {
const enumsMap = await this.getEnumsMap()
const enums: EnumMetadata[] = []
for (const [name, values] of enumsMap) {
enums.push({ name, values })
}
return enums.sort((a, b) => {
return a.name.localeCompare(b.name)
})
}
public getEnumsTypegen(enums: EnumMetadata[]): string[] {
const result: string[] = []
for (const enumMetadata of enums) {
const enumName = enumMetadata.name
result.push(
`export type ${enumName} = ${enumMetadata.values
.map((value) => {
return `"${value}"`
})
.join(" | ")}`,
"",
)
}
return result
}
public abstract typegen(): Promise<{
lines: string[]
enums: EnumMetadata[]
tables: TableMetadata[]
}>
}
export class KyselyTypegenPostgresDialect extends KyselyTypegenDialect {
public database: KyselyTypegenDialect["database"]
// These types have been found through experimentation in Adminer and in the 'pg' source code.
public readonly scalars: Record<string, string> = {
bit: "string",
bool: "boolean",
box: "string",
bpchar: "string",
bytea: "Buffer",
cidr: "string",
date: "Timestamp",
float4: "number",
float8: "number",
inet: "string",
int2: "number",
int4: "number",
int8: "Int8",
json: "Json",
jsonb: "Json",
line: "string",
lseg: "string",
macaddr: "string",
money: "string",
numeric: "Numeric",
oid: "number",
path: "string",
polygon: "string",
text: "string",
time: "string",
timestamp: "Timestamp",
timestamptz: "Timestamp",
timetz: "string",
tsquery: "string",
tsvector: "string",
uuid: "string",
varbit: "string",
varchar: "string",
xml: "string",
}
public constructor(input: { database: KyselyTypegenDialect["database"] }) {
super()
this.database = input.database
}
protected async getEnumsMap(): Promise<Map<string, string[]>> {
const rows = await this.database
.withoutPlugins()
.selectFrom("pg_type as type")
.innerJoin("pg_enum as enum", "type.oid", "enum.enumtypid")
.innerJoin("pg_catalog.pg_namespace as namespace", "namespace.oid", "type.typnamespace")
.select(["type.typname as enumName", "enum.enumlabel as enumValue"])
.execute()
const enums = new Map<string, string[]>()
for (const row of rows) {
const data = row as { enumName: string; enumValue: string }
enums.set(
data.enumName,
[...(enums.get(data.enumName) ?? []), data.enumValue].sort((a, b) => {
return a.localeCompare(b)
}),
)
}
return enums
}
/**
* Generate TypeScript types based on the database schema, including tables and enums.
*/
public async typegen(): Promise<{
lines: string[]
enums: EnumMetadata[]
tables: TableMetadata[]
}> {
const [tables, enums] = await Promise.all([this.getTables(), this.getEnums()])
const lines: string[] = [
`// This file was automatically generated by \`kysely-typegen\`.`,
"// Do not edit this file manually.",
"",
'import type { ColumnType } from "kysely"',
"",
"export type Generated<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>",
"",
"export type Timestamp = ColumnType<Date, Date | string, Date | string>",
"",
"export type Numeric = ColumnType<string, number | string, number | string>",
"",
"export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>",
"",
"export type Json = JsonValue",
"",
"export type JsonArray = JsonValue[]",
"",
"export interface JsonObject {",
" [x: string]: JsonValue | undefined",
"}",
"",
"export type JsonPrimitive = boolean | number | string | null",
"",
"export type JsonValue = JsonArray | JsonObject | JsonPrimitive",
"",
]
lines.push(...this.getEnumsTypegen(enums))
lines.push(...this.getTablesTypegen(tables, enums))
lines.push("export interface DB {")
for (const table of tables) {
lines.push(` ${table.name}: ${table.name}`)
}
lines.push("}")
return { lines, enums, tables }
}
}