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

如何提取类属性的类型?动态解析装饰器标注的模型类

问题描述

这是我的类:

class Game extends BaseModel {
    @column({ isPrimary: true })
    declare id: number
    
    @column()
    declare name: string

    @column()
    declare slug: string
}

我正尝试构建一个解析器,期望得到如下结果:

{
    id: {
        type: "string",
        props: {
            isPrimary: true
        }
    },
    name: {
        type: "string"
    },
    slug: {
        type: "string"
    },
}

BaseModel提供静态映射表可获取装饰器列属性,但无法获取类型信息。因需构建适配任意模型的动态解析器,无法实例化类,特此寻求解决方案。


解决方案

方案1:通过reflect-metadata在装饰器中存储类型信息

TypeScript支持结合reflect-metadata库获取属性的类型元数据,需要先在tsconfig.json中开启两个配置:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

如果可以修改@column装饰器的实现,在装饰器中同时收集类型信息并存储到类的静态元数据中:

import 'reflect-metadata';

function column(props: Record<string, any> = {}) {
  return (target: any, propertyKey: string) => {
    // 从TypeScript生成的元数据中获取属性类型
    const typeConstructor = Reflect.getMetadata('design:type', target, propertyKey);
    const typeName = typeConstructor.name.toLowerCase();

    // 将类型与装饰器属性一起存入类的静态映射表(复用BaseModel的存储逻辑或新增)
    const modelClass = target.constructor;
    if (!modelClass.columnSchema) {
      modelClass.columnSchema = {};
    }
    modelClass.columnSchema[propertyKey] = {
      type: typeName,
      ...(Object.keys(props).length ? { props } : {})
    };
  };
}

之后解析器直接读取类的静态元数据即可:

function parseModel(model: typeof BaseModel) {
  return model.columnSchema || {};
}

// 使用示例
const gameSchema = parseModel(Game);

如果无法修改原@column装饰器,可以额外添加一个类装饰器来补全类型信息,或者扩展BaseModel的静态方法来批量读取元数据并合并到现有映射表中。

方案2:编译时源码解析(ts-morph)

如果运行时获取类型受限,可以用ts-morph直接解析TypeScript源码,提取属性类型和装饰器信息:

import { Project } from 'ts-morph';

async function generateModelSchema(filePath: string, className: string) {
  const project = new Project();
  const sourceFile = project.addSourceFileAtPath(filePath);
  const modelClass = sourceFile.getClass(className);
  if (!modelClass) return {};

  const schema: Record<string, any> = {};
  const properties = modelClass.getProperties();

  for (const prop of properties) {
    // 提取@column装饰器的参数
    const columnDecorator = prop.getDecorator('column');
    const rawProps = columnDecorator?.getArguments()[0].getText();
    const props = rawProps ? JSON.parse(rawProps) : {};

    // 提取属性类型
    const typeName = prop.getType().getText().toLowerCase();

    schema[prop.getName()] = {
      type: typeName,
      ...(Object.keys(props).length ? { props } : {})
    };
  }

  return schema;
}

// 使用示例
generateModelSchema('./src/models/Game.ts', 'Game').then(console.log);

这种方式不需要运行时依赖,适合在构建阶段预生成所有模型的Schema,避免运行时开销。

方案3:TypeScript自定义Transformer

最底层的方案是编写TypeScript自定义Transformer,在编译过程中自动为每个模型类生成静态Schema方法。这种方式性能最优,但实现复杂度较高,适合大型项目。

核心思路是在编译时遍历AST,识别带有@column装饰器的属性,提取类型和装饰器参数,生成对应的Schema对象并注入到类的静态方法中,比如getSchema(),运行时直接调用即可获取结果。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 17:37:15