TypeScript中能否通过反射实例化泛型类型的属性?
解决方案
需求可以实现,核心需要处理TypeScript泛型的运行时擦除问题:泛型T的类型信息仅存在于编译阶段,运行时无法直接获取T的属性类型,因此需要手动标记需要实例化的类属性,补充运行时可读取的元数据即可。
实现思路
通过装饰器或者静态属性映射,提前标记需要实例化的嵌套类属性对应的构造函数,fromJSON方法遍历属性时读取提前标记的元数据,判断是否需要执行实例化操作。
代码实现(装饰器版本,推荐)
该方案语法更简洁,需要先在tsconfig.json中开启experimentalDecorators配置:
{ "compilerOptions": { "experimentalDecorators": true } }
1. 定义基础工具和抽象类
import 'reflect-metadata'; // 标记嵌套类属性的装饰器,传入对应类的构造函数 const Nested = (ctor: new (...args: any[]) => any) => { return (target: any, propertyKey: string) => { Reflect.defineMetadata('nested:constructor', ctor, target, propertyKey); }; }; // JSON入参的类型定义,支持嵌套结构 type JSONClassDictionary<T> = { [K in keyof T]?: T[K] extends object ? JSONClassDictionary<T[K]> | T[K] : T[K]; }; abstract class JSONClass<T> { constructor(json: JSONClassDictionary<T>) { this.fromJSON(json); } fromJSON(json: JSONClassDictionary<T>): void { for (const key in json) { if (!Object.prototype.hasOwnProperty.call(json, key)) continue; const jsonValue = json[key]; // 读取当前属性标记的嵌套类构造函数 const nestedCtor = Reflect.getMetadata('nested:constructor', this, key as string); if (nestedCtor && typeof jsonValue === 'object' && jsonValue !== null) { // 是嵌套类属性,执行实例化 this[key as keyof this] = new nestedCtor(jsonValue) as this[keyof this]; } else { // 原始类型直接赋值 this[key as keyof this] = jsonValue as this[keyof this]; } } } }
2. 业务类使用示例
// 嵌套的PermissionGroup类,也继承JSONClass class PermissionGroup extends JSONClass<PermissionGroup> { id: number; action: string; } class UserRole extends JSONClass<UserRole> { id: number; name: string; // 用@Nested标记该属性需要用PermissionGroup实例化 @Nested(PermissionGroup) permissionGroup: PermissionGroup; }
3. 调用示例
const role = new UserRole({ id: 1, name: "Admin", permissionGroup: { id: 1, action: ".." } }); // 验证实例化结果:permissionGroup已经是PermissionGroup的实例 console.log(role.permissionGroup instanceof PermissionGroup); // 输出 true
代码实现(无装饰器版本)
如果不想使用装饰器,可以用静态属性手动声明嵌套属性的构造函数映射:
type JSONClassDictionary<T> = { [K in keyof T]?: T[K] extends object ? JSONClassDictionary<T[K]> | T[K] : T[K]; }; abstract class JSONClass<T> { // 子类需要实现该静态属性,声明嵌套属性对应的构造函数 static nestedProperties: Record<string, new (...args: any[]) => any> = {}; constructor(json: JSONClassDictionary<T>) { this.fromJSON(json); } fromJSON(json: JSONClassDictionary<T>): void { const nestedCtors = (this.constructor as typeof JSONClass).nestedProperties; for (const key in json) { if (!Object.prototype.hasOwnProperty.call(json, key)) continue; const jsonValue = json[key]; const nestedCtor = nestedCtors[key]; if (nestedCtor && typeof jsonValue === 'object' && jsonValue !== null) { this[key as keyof this] = new nestedCtor(jsonValue) as this[keyof this]; } else { this[key as keyof this] = jsonValue as this[keyof this]; } } } } // 子类使用示例 class UserRole extends JSONClass<UserRole> { // 静态属性声明嵌套属性的构造函数 static nestedProperties = { permissionGroup: PermissionGroup }; id: number; name: string; permissionGroup: PermissionGroup; }
问题解答
- 无法直接通过泛型
T判断属性是否为类类型,因为泛型运行时会被擦除,所以需要通过上述手动标记的方式补充运行时可识别的元数据,即可区分原始类型属性和需要实例化的类属性。 - 标记后可以直接获取到属性对应的构造函数,传入JSON属性值执行
new操作即可完成初始化,支持任意层级的嵌套类实例化,只要嵌套的类也继承JSONClass即可。
内容的提问来源于stack exchange,提问作者Constantin
相关产品推荐
相关产品推荐

