You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将TypeScript接口键转换为类型值,实现自动建表推断?

基于TypeScript接口自动生成数据库表结构的实现方案

我正尝试在数据库表类中实现进阶功能,希望用户仅传入行接口(必要时可传类),让表类自动推断如何创建数据库表。相关代码示例如下:

interface Row {
   rowid: number
}

class Table<T extends Row> {
   getRowDefinition(){
     // 这里需要实现用T生成列定义的逻辑
   }
 
   createTable(){
     const defs = this.getRowDefinition();
     const cols = defs.map(def => {
        switch(def.type){
          case 'string':
           return `${def.name} TEXT`;
          case 'number':
           return `${def.name} INTEGER`;
        }
     });
     const sql = 'CREATE TABLE ...'; // 拼接cols生成完整SQL
   }
}

interface MyRow extends Row {
  name: string
  age: number
}

class MyTable extends Table<MyRow> {}

核心问题说明

TypeScript的类型信息仅存在于编译阶段,泛型参数T在编译后会被完全擦除,所以无法直接通过泛型T在运行时获取行结构的字段和类型信息。要实现自动推断,必须借助运行时可访问的元数据,以下是两种可行的实现方案:


方案一:手动传入字段元数据

既然接口没有运行时表示,我们可以让用户在创建Table实例时,手动传入字段的类型定义对象,以此作为表结构的依据:

interface Row {
  rowid: number
}

// 定义列结构的描述类型
interface ColumnDefinition {
  name: string;
  type: 'string' | 'number';
}

class Table<T extends Row> {
  private columnDefs: ColumnDefinition[];

  constructor(columnDefs: ColumnDefinition[]) {
    // 确保rowid字段被包含(如果用户没传的话自动添加)
    const hasRowid = columnDefs.some(def => def.name === 'rowid');
    this.columnDefs = hasRowid ? columnDefs : [...columnDefs, { name: 'rowid', type: 'number' }];
  }

  createTable(tableName: string) {
    const cols = this.columnDefs.map(def => {
      switch(def.type){
        case 'string':
          return `${def.name} TEXT`;
        case 'number':
          return `${def.name} INTEGER`;
        default:
          throw new Error(`不支持的字段类型:${def.type}`);
      }
    });
    const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (${cols.join(', ')})`;
    console.log(sql);
    // 这里可以添加执行SQL的实际逻辑
  }
}

interface MyRow extends Row {
  name: string;
  age: number;
}

// 创建实例时传入字段元数据
const myTable = new Table<MyRow>([
  { name: 'name', type: 'string' },
  { name: 'age', type: 'number' }
]);
myTable.createTable('my_table');

方案二:使用类+装饰器标记字段

如果允许用户用类代替接口,我们可以通过装饰器来标记每个字段的类型,在运行时从类的构造函数中提取这些元数据:

interface Row {
  rowid: number;
}

// 装饰器工厂:用于标记字段对应的数据库类型
function Column(type: 'string' | 'number') {
  return function(target: any, propertyKey: string) {
    // 将字段定义挂载到类的构造函数上
    if (!target.constructor.columnDefs) {
      target.constructor.columnDefs = [];
    }
    target.constructor.columnDefs.push({ name: propertyKey, type });
  };
}

class Table<T extends Row> {
  private columnDefs: { name: string; type: 'string' | 'number' }[];

  constructor(rowClass: new () => T) {
    // 合并默认的rowid字段和类中标记的字段
    this.columnDefs = [
      { name: 'rowid', type: 'number' },
      ...(rowClass.columnDefs || [])
    ];
  }

  createTable(tableName: string) {
    const cols = this.columnDefs.map(def => {
      switch(def.type){
        case 'string':
          return `${def.name} TEXT`;
        case 'number':
          return `${def.name} INTEGER`;
        default:
          throw new Error(`不支持的字段类型:${def.type}`);
      }
    });
    const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (${cols.join(', ')})`;
    console.log(sql);
    // 这里可以添加执行SQL的实际逻辑
  }
}

// 用类代替接口,通过装饰器标记字段类型
class MyRow implements Row {
  rowid: number;
  
  @Column('string')
  name: string;
  
  @Column('number')
  age: number;
}

// 传入类的构造函数即可生成表
const myTable = new Table(MyRow);
myTable.createTable('my_table');

内容的提问来源于stack exchange,提问作者samala7800

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 16:54:51