TypeScript中能否读取类定义属性?如何实现泛型遍历?
TypeScript中枚举类定义属性的方案
首先要明确:TypeScript的类型信息仅存在于编译阶段,运行时会被完全擦除,所以无法直接从泛型类型T中获取类的属性列表。你之前用Object.getOwnPropertyNames(new TestClass())没结果,是因为TestClass里只有类型声明,没有给属性赋值——这类属性不会被添加到实例对象中,自然无法被枚举。
下面是几种可行的替代方案:
1. 给类属性添加默认值
只要给属性赋初始值,实例化后这些属性就会存在于对象中,就能用Object.getOwnPropertyNames或Object.keys枚举:
class TestClass { public testProp: string = ''; public testProp2: number = 0; public testProp3: boolean = false; public testProp4: Date = new Date(); public testProp5: any = null; } function someMethod<T>(instance: T) { return Object.getOwnPropertyNames(instance); } // 调用后会输出所有属性名 console.log(someMethod(new TestClass()));
2. 使用装饰器收集属性
通过自定义装饰器标记类的属性,并利用Reflect API记录属性信息,这样即使属性没有初始值,也能在运行时拿到属性列表(需要在tsconfig.json中启用experimentalDecorators和emitDecoratorMetadata):
import 'reflect-metadata'; // 定义唯一标识用于存储属性列表 const PROPERTIES_KEY = Symbol('properties'); // 自定义装饰器,标记需要收集的属性 function TrackProperty() { return (target: any, propertyKey: string) => { const existingProps = Reflect.getMetadata(PROPERTIES_KEY, target) || []; existingProps.push(propertyKey); Reflect.defineMetadata(PROPERTIES_KEY, existingProps, target); }; } class TestClass { @TrackProperty() public testProp: string; @TrackProperty() public testProp2: number; @TrackProperty() public testProp3: boolean; @TrackProperty() public testProp4: Date; @TrackProperty() public testProp5: any; } function someMethod<T>(cls: new () => T) { // 从类的原型中取出收集到的属性列表 return Reflect.getMetadata(PROPERTIES_KEY, cls.prototype); } // 调用后输出所有标记的属性名 console.log(someMethod(TestClass));
3. 传递属性模板对象
如果可以接受传递一个包含目标类型所有属性的模板对象,就能直接枚举这个对象的属性,同时通过泛型推导获取类型:
function someMethod<T>(props: T) { return Object.keys(props) as Array<keyof T>; } // 传递包含所有属性的模板,自动推导类型 const propsList = someMethod({ testProp: '', testProp2: 0, testProp3: false, testProp4: new Date(), testProp5: null }); console.log(propsList);
关于构造带默认值的对象
TypeScript没有内置的辅助函数直接生成带默认值的类实例,但可以结合上面的装饰器方案实现一个工具函数,自动给标记的属性赋默认值:
function createDefaultInstance<T>(cls: new () => T): T { const instance = new cls(); const properties = Reflect.getMetadata(PROPERTIES_KEY, cls.prototype) || []; properties.forEach((prop: string) => { // 根据属性类型的默认规则赋值 switch (typeof instance[prop]) { case 'string': instance[prop] = '' as any; break; case 'number': instance[prop] = 0 as any; break; case 'boolean': instance[prop] = false as any; break; case 'object': // 特殊处理Date类型 if (cls.prototype[prop] instanceof Date) { instance[prop] = new Date() as any; } else { instance[prop] = null as any; } break; } }); return instance; } // 使用示例 const defaultInstance = createDefaultInstance(TestClass); console.log(Object.getOwnPropertyNames(defaultInstance)); // 输出所有属性名
内容的提问来源于stack exchange,提问作者Jeff Tian
相关产品推荐
相关产品推荐

