Drizzle ORM中PgTable名称获取异常:类型与运行时不符的解决问询
解决Drizzle ORM外键工具函数运行时表名获取错误问题
问题分析
你遇到的TypeError: Cannot read properties of undefined (reading 'name')核心原因有两个:
- TypeScript类型定义里
PgTableWithColumns声明了_属性,但Drizzle在运行时并未将表名暴露在_字段中,而是存放在内部私有Symbol键Symbol(drizzle:BaseName)下,直接访问table._.name自然会拿到undefined。 - 代码里存在拼写错误:
snakeCase()}_id应该是snakeCase(tableName)_id`,这也会导致后续列名生成错误。
解决方案
方案1:通过内部Symbol获取表名(适配当前Drizzle版本)
虽然Drizzle没有公开这个Symbol,但可以通过遍历对象的Symbol属性定位到它,从而获取表名:
import { type PgColumn, type PgTableWithColumns, pgTableCreator, serial, bigint, varchar, } from "drizzle-orm/pg-core"; import { camelCase, snakeCase } from "lodash"; export const pgTable = pgTableCreator((name) => { return `some-app_${name}`; }); export function id() { return serial("id").primaryKey(); } type TableWithId = PgTableWithColumns<{ name: string; schema: string | undefined; dialect: "pg"; columns: { id: PgColumn; }; }>; // 定位Drizzle内部的表名Symbol const getTableNameSymbol = (table: TableWithId): symbol => { return Object.getOwnPropertySymbols(table).find(sym => sym.description === "drizzle:BaseName")!; }; export function fk(...relations: (TableWithId | TableWithId[])[]) { return Object.fromEntries( relations.flatMap((entry) => { const isOptional = Array.isArray(entry); const tables = isOptional ? entry : [entry]; return tables.map((table) => { // 获取原始表名 const tableName = table[getTableNameSymbol(table)] as string; // 生成蛇形命名的外键列名 const columnName = `${snakeCase(tableName)}_id`; // 生成驼峰式的模型属性名 const propertyName = `${camelCase(tableName)}Id`; const column = bigint(columnName, { mode: "number" }).references(() => table.id); return [propertyName, isOptional ? column : column.notNull()]; }); }), ); } // 补充示例timestamps函数(按需使用) export function timestamps() { return { createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), }; }
方案2:自定义表创建函数,附加可访问的表名(更稳定)
依赖内部Symbol的方式可能随Drizzle版本更新失效,更稳妥的方式是在创建表时手动添加可访问的表名属性:
import { type PgColumn, type PgTableWithColumns, pgTableCreator, serial, bigint, } from "drizzle-orm/pg-core"; import { camelCase, snakeCase } from "lodash"; const basePgTable = pgTableCreator((name) => `some-app_${name}`); // 自定义表创建函数,附加公开的表名属性 export const pgTable = <T extends Record<string, any>>(name: string, columns: T) => { const table = basePgTable(name, columns); // 添加可直接访问的表名属性 (table as any)._publicTableName = name; return table as typeof table & { _publicTableName: string }; }; export function id() { return serial("id").primaryKey(); } // 更新类型定义,包含自定义表名属性 type TableWithId = PgTableWithColumns<{ name: string; schema: string | undefined; dialect: "pg"; columns: { id: PgColumn }; }> & { _publicTableName: string }; export function fk(...relations: (TableWithId | TableWithId[])[]) { return Object.fromEntries( relations.flatMap((entry) => { const isOptional = Array.isArray(entry); const tables = isOptional ? entry : [entry]; return tables.map((table) => { const tableName = table._publicTableName; const columnName = `${snakeCase(tableName)}_id`; const propertyName = `${camelCase(tableName)}Id`; const column = bigint(columnName, { mode: "number" }).references(() => table.id); return [propertyName, isOptional ? column : column.notNull()]; }); }), ); }
验证使用
按你的预期方式使用即可,修复后不会再出现表名未定义的错误:
// 先定义关联表示例 export const phone = pgTable("phones", { id: id(), number: varchar("number", { length: 20 }).notNull() }); export const email = pgTable("emails", { id: id(), address: varchar("address", { length: 255 }).notNull() }); export const address = pgTable("addresses", { id: id(), street: varchar("street", { length: 255 }).notNull() }); // 主表使用fk函数 export const contact = pgTable("contacts", { id: id(), firstName: varchar("first_name", { length: 128 }).notNull(), lastName: varchar("last_name", { length: 128 }).notNull(), ...fk(phone, [email], [address]), ...timestamps(), });
内容的提问来源于stack exchange,提问作者kaan_atakan
相关产品推荐
相关产品推荐

