Java泛型在运行时无法获取具体类型参数,因为编译期执行类型擦除,泛型信息被替换为原始类型(如Object或上界),字节码中不保留具体类型参数,反射只能获取擦除后的类型。

Reflect 无法获取泛型方法的运行时参数类型 —— 因为 TypeScript 的泛型在编译后完全擦除,JavaScript 运行时根本不存在“泛型类型信息”。
为什么 Reflect.getMetadata 拿不到泛型参数类型
TypeScript 泛型是纯编译期特性。tsc 编译时会把 <t></t>、Array<string></string> 全部抹掉,生成的 JS 里只有 function foo(arg) { ... } 和原始 JavaScript 类型(如 object、function)。即使你用 reflect-metadata 并加了 @Reflect.metadata 装饰器,Reflect.getMetadata('design:paramtypes', target, 'methodName') 返回的也只是擦除后的基础类型(例如 [Object, Function]),不会包含 string、User<number></number> 这类泛型细节。
- 装饰器元数据只保留
design:paramtypes、design:returntype等有限键,且仅对应编译后可推断的“顶层类型” -
Array<t></t>擦除为Array→ 元数据中记为Array构造函数,不带T -
Promise<date></date>擦除为Promise→ 元数据中就是Promise,Date信息彻底丢失
哪些场景下你以为能拿到,其实拿不到
常见误判包括:
- 在类方法上写
async method<t>(data: T[]): Promise<t></t></t>,然后试图用Reflect.getMetadata('design:paramtypes', ...)提取T—— 实际返回的是[Array]和Promise,没有T - 用
ts-morph或typescript模块在 Node 里解析源码,误以为这是“运行时” —— 这属于编译分析阶段,不是 Reflect 所指的 JS 运行时 - 给泛型参数加
class-validator的@Type(() => Foo),以为这会注入到 Reflect 元数据里 —— 它只影响验证逻辑,不修改design:paramtypes
如果真需要运行时泛型类型,只能手动传或约定
没有银弹,但有可控路径:
- 显式传入构造函数:把
<t>(data: T[])</t>改成(data: any[], itemType: new () => T),调用时传User类本身,再用itemType.name或instanceof做判断 - 用 Symbol 作为元数据 key 存自定义类型描述:
Reflect.defineMetadata('my:param-generic', { index: 0, type: 'User' }, target, 'method'),自己维护这套映射 - 对简单场景(如 API 响应解析),直接用
zod或io-ts定义运行时 schema,绕过 Reflect —— 它们本质是“手动写的类型契约”,而非自动提取
容易被忽略的关键点
很多人卡在配了 "emitDecoratorMetadata": true 和 reflect-metadata polyfill 就以为万事大吉。但关键不在“有没有装饰器”,而在于 tsc 根本不生成泛型相关的元数据 —— 它连语法树里都删掉了泛型节点。哪怕你用 Babel + @babel/plugin-transform-typescript,只要没启用类似 babel-plugin-transform-typescript-generics 这种非常规插件(且该插件也不标准),结果一样。
真正要跑通,得接受一个事实:运行时类型 = 你愿意为它多写多少代码,而不是靠反射自动猜出来。

















