如何提取类属性的类型?动态解析装饰器标注的模型类
问题描述
这是我的类:
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
相关产品推荐
相关产品推荐

