Skip to content

Commit

Permalink
fix: Add capability for typescript type gen to reference table row ty…
Browse files Browse the repository at this point in the history
…pes (#533)

Co-authored-by: Bobbie Soedirgo <bobbie@soedirgo.dev>
  • Loading branch information
joeally and soedirgo committed Mar 13, 2024
1 parent ae27189 commit 2ddd618
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 36 deletions.
82 changes: 58 additions & 24 deletions src/server/templates/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import type {
PostgresColumn,
PostgresFunction,
PostgresSchema,
PostgresTable,
PostgresType,
PostgresView,
} from '../../lib/index.js'
import type { GeneratorMetadata } from '../../lib/generators.js'

Expand Down Expand Up @@ -80,19 +82,20 @@ export type Database = {
${[
...columnsByTableId[table.id].map(
(column) =>
`${JSON.stringify(column.name)}: ${pgTypeToTsType(
column.format,
`${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
types,
schemas
)} ${column.is_nullable ? '| null' : ''}`
schemas,
tables,
views,
})} ${column.is_nullable ? '| null' : ''}`
),
...schemaFunctions
.filter((fn) => fn.argument_types === table.name)
.map((fn) => {
const type = types.find(({ id }) => id === fn.return_type_id)
let tsType = 'unknown'
if (type) {
tsType = pgTypeToTsType(type.name, types, schemas)
tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
}
return `${JSON.stringify(fn.name)}: ${tsType} | null`
}),
Expand All @@ -116,7 +119,7 @@ export type Database = {
output += ':'
}
output += pgTypeToTsType(column.format, types, schemas)
output += pgTypeToTsType(column.format, { types, schemas, tables, views })
if (column.is_nullable) {
output += '| null'
Expand All @@ -133,7 +136,7 @@ export type Database = {
return `${output}?: never`
}
output += `?: ${pgTypeToTsType(column.format, types, schemas)}`
output += `?: ${pgTypeToTsType(column.format, { types, schemas, tables, views })}`
if (column.is_nullable) {
output += '| null'
Expand Down Expand Up @@ -180,11 +183,12 @@ export type Database = {
Row: {
${columnsByTableId[view.id].map(
(column) =>
`${JSON.stringify(column.name)}: ${pgTypeToTsType(
column.format,
`${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
types,
schemas
)} ${column.is_nullable ? '| null' : ''}`
schemas,
tables,
views,
})} ${column.is_nullable ? '| null' : ''}`
)}
}
${
Expand All @@ -197,7 +201,7 @@ export type Database = {
return `${output}?: never`
}
output += `?: ${pgTypeToTsType(column.format, types, schemas)} | null`
output += `?: ${pgTypeToTsType(column.format, { types, schemas, tables, views })} | null`
return output
})}
Expand All @@ -210,7 +214,7 @@ export type Database = {
return `${output}?: never`
}
output += `?: ${pgTypeToTsType(column.format, types, schemas)} | null`
output += `?: ${pgTypeToTsType(column.format, { types, schemas, tables, views })} | null`
return output
})}
Expand Down Expand Up @@ -279,7 +283,7 @@ export type Database = {
const type = types.find(({ id }) => id === type_id)
let tsType = 'unknown'
if (type) {
tsType = pgTypeToTsType(type.name, types, schemas)
tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
}
return { name, type: tsType, has_default }
})
Expand All @@ -299,7 +303,7 @@ export type Database = {
const type = types.find(({ id }) => id === type_id)
let tsType = 'unknown'
if (type) {
tsType = pgTypeToTsType(type.name, types, schemas)
tsType = pgTypeToTsType(type.name, { types, schemas, tables, views })
}
return { name, type: tsType }
})
Expand All @@ -319,19 +323,20 @@ export type Database = {
return `{
${columnsByTableId[relation.id].map(
(column) =>
`${JSON.stringify(column.name)}: ${pgTypeToTsType(
column.format,
`${JSON.stringify(column.name)}: ${pgTypeToTsType(column.format, {
types,
schemas
)} ${column.is_nullable ? '| null' : ''}`
schemas,
tables,
views,
})} ${column.is_nullable ? '| null' : ''}`
)}
}`
}
// Case 3: returns base/array/composite/enum type.
const type = types.find(({ id }) => id === return_type_id)
if (type) {
return pgTypeToTsType(type.name, types, schemas)
return pgTypeToTsType(type.name, { types, schemas, tables, views })
}
return 'unknown'
Expand Down Expand Up @@ -367,7 +372,7 @@ export type Database = {
const type = types.find(({ id }) => id === type_id)
let tsType = 'unknown'
if (type) {
tsType = `${pgTypeToTsType(type.name, types, schemas)} | null`
tsType = `${pgTypeToTsType(type.name, { types, schemas, tables, views })} | null`
}
return `${JSON.stringify(name)}: ${tsType}`
})}
Expand Down Expand Up @@ -470,8 +475,17 @@ export type Enums<
// TODO: Make this more robust. Currently doesn't handle range types - returns them as unknown.
const pgTypeToTsType = (
pgType: string,
types: PostgresType[],
schemas: PostgresSchema[]
{
types,
schemas,
tables,
views,
}: {
types: PostgresType[]
schemas: PostgresSchema[]
tables: PostgresTable[]
views: PostgresView[]
}
): string => {
if (pgType === 'bool') {
return 'boolean'
Expand Down Expand Up @@ -501,7 +515,7 @@ const pgTypeToTsType = (
} else if (pgType === 'record') {
return 'Record<string, unknown>'
} else if (pgType.startsWith('_')) {
return `(${pgTypeToTsType(pgType.substring(1), types, schemas)})[]`
return `(${pgTypeToTsType(pgType.substring(1), { types, schemas, tables, views })})[]`
} else {
const enumType = types.find((type) => type.name === pgType && type.enums.length > 0)
if (enumType) {
Expand All @@ -523,6 +537,26 @@ const pgTypeToTsType = (
return 'unknown'
}

const tableRowType = tables.find((table) => table.name === pgType)
if (tableRowType) {
if (schemas.some(({ name }) => name === tableRowType.schema)) {
return `Database[${JSON.stringify(tableRowType.schema)}]['Tables'][${JSON.stringify(
tableRowType.name
)}]['Row']`
}
return 'unknown'
}

const viewRowType = views.find((view) => view.name === pgType)
if (viewRowType) {
if (schemas.some(({ name }) => name === viewRowType.schema)) {
return `Database[${JSON.stringify(viewRowType.schema)}]['Views'][${JSON.stringify(
viewRowType.name
)}]['Row']`
}
return 'unknown'
}

return 'unknown'
}
}
5 changes: 5 additions & 0 deletions test/db/00-init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,8 @@ create table user_details (
create view a_view as select id from users;

create table empty();

create table table_with_other_tables_row_type (
col1 user_details,
col2 a_view
);
24 changes: 12 additions & 12 deletions test/lib/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,19 @@ test('list', async () => {
],
"relationships": [
{
"constraint_name": "todos_user-id_fkey",
"source_column_name": "user-id",
"constraint_name": "user_details_user_id_fkey",
"source_column_name": "user_id",
"source_schema": "public",
"source_table_name": "todos",
"source_table_name": "user_details",
"target_column_name": "id",
"target_table_name": "users",
"target_table_schema": "public",
},
{
"constraint_name": "user_details_user_id_fkey",
"source_column_name": "user_id",
"constraint_name": "todos_user-id_fkey",
"source_column_name": "user-id",
"source_schema": "public",
"source_table_name": "user_details",
"source_table_name": "todos",
"target_column_name": "id",
"target_table_name": "users",
"target_table_schema": "public",
Expand Down Expand Up @@ -178,19 +178,19 @@ test('list without columns', async () => {
],
"relationships": [
{
"constraint_name": "todos_user-id_fkey",
"source_column_name": "user-id",
"constraint_name": "user_details_user_id_fkey",
"source_column_name": "user_id",
"source_schema": "public",
"source_table_name": "todos",
"source_table_name": "user_details",
"target_column_name": "id",
"target_table_name": "users",
"target_table_schema": "public",
},
{
"constraint_name": "user_details_user_id_fkey",
"source_column_name": "user_id",
"constraint_name": "todos_user-id_fkey",
"source_column_name": "user-id",
"source_schema": "public",
"source_table_name": "user_details",
"source_table_name": "todos",
"target_column_name": "id",
"target_table_name": "users",
"target_table_schema": "public",
Expand Down
30 changes: 30 additions & 0 deletions test/server/typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,21 @@ test('typegen', async () => {
},
]
}
table_with_other_tables_row_type: {
Row: {
col1: Database["public"]["Tables"]["user_details"]["Row"] | null
col2: Database["public"]["Views"]["a_view"]["Row"] | null
}
Insert: {
col1?: Database["public"]["Tables"]["user_details"]["Row"] | null
col2?: Database["public"]["Views"]["a_view"]["Row"] | null
}
Update: {
col1?: Database["public"]["Tables"]["user_details"]["Row"] | null
col2?: Database["public"]["Views"]["a_view"]["Row"] | null
}
Relationships: []
}
todos: {
Row: {
details: string | null
Expand Down Expand Up @@ -539,6 +554,21 @@ test('typegen w/ one-to-one relationships', async () => {
},
]
}
table_with_other_tables_row_type: {
Row: {
col1: Database["public"]["Tables"]["user_details"]["Row"] | null
col2: Database["public"]["Views"]["a_view"]["Row"] | null
}
Insert: {
col1?: Database["public"]["Tables"]["user_details"]["Row"] | null
col2?: Database["public"]["Views"]["a_view"]["Row"] | null
}
Update: {
col1?: Database["public"]["Tables"]["user_details"]["Row"] | null
col2?: Database["public"]["Views"]["a_view"]["Row"] | null
}
Relationships: []
}
todos: {
Row: {
details: string | null
Expand Down

0 comments on commit 2ddd618

Please sign in to comment.