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

如何遍历TypeScript类的属性并获取各属性的预期类型?

问题解答

TypeScript编译后会丢失类型信息,所以直接遍历未实例化的类本身,无法在运行时获取属性的类型标注(比如number、string、Price[]这类信息)。不过可以通过以下几种方案实现你的需求:

1. 装饰器+元数据方案(最可靠)

借助reflect-metadata库,结合装饰器给类属性注入类型元数据,就能在运行时读取属性的具体类型。

实现步骤:

  • 安装依赖:npm install reflect-metadata
  • 在tsconfig.json中开启emitDecoratorMetadata和experimentalDecorators选项
  • 编写装饰器并给类属性标记类型:
import 'reflect-metadata';

// 定义装饰器,用于记录属性类型
function Type(type: any) {
  return function(target: any, propertyKey: string) {
    Reflect.defineMetadata('design:type', type, target, propertyKey);
    // 数组类型额外标记元素类型
    if (type === Array) {
      Reflect.defineMetadata('design:elementType', arguments[1], target, `${propertyKey}:element`);
    }
  };
}

export class Price {}

export class Product {
  @Type(Number)
  id: number;

  @Type(String)
  title: string;

  @Type(Array)
  @Type(Price)
  prices: Price[];

  constructor() {
    this.id = null;
    this.title = null;
    this.prices = [];
  }
}

// 获取属性类型的工具函数
function getPropertyTypes(target: any): Record<string, string> {
  const propertyNames = Object.getOwnPropertyNames(target.prototype).filter(name => name !== 'constructor');
  const types: Record<string, string> = {};
  
  propertyNames.forEach(name => {
    const type = Reflect.getMetadata('design:type', target.prototype, name);
    if (type === Array) {
      const elementType = Reflect.getMetadata('design:elementType', target.prototype, `${name}:element`);
      types[name] = `${elementType.name}[]`;
    } else {
      types[name] = type.name.toLowerCase();
    }
  });
  
  return types;
}

// 使用示例
const productTypes = getPropertyTypes(Product);
console.log(productTypes);
// 输出:{ id: 'number', title: 'string', prices: 'Price[]' }

2. 手动维护类型配置(轻量方案)

如果不想引入额外依赖,可以手动为每个类编写类型配置对象,映射时直接读取配置:

export class Price {}

export class Product {
  id: number;
  title: string;
  prices: Price[];

  constructor() {
    this.id = null;
    this.title = null;
    this.prices = [];
  }
}

// 手动定义Product的类型映射配置
const ProductTypeConfig = {
  id: 'number',
  title: 'string',
  prices: { type: 'array', elementModel: Price }
};

// 映射工具函数
function mapToModel(apiData: any, config: any, modelClass: any) {
  const model = new modelClass();
  Object.keys(config).forEach(key => {
    const propConfig = config[key];
    if (propConfig.type === 'array' && propConfig.elementModel) {
      model[key] = apiData[key]?.map(item => mapToModel(item, getModelConfig(propConfig.elementModel), propConfig.elementModel)) || [];
    } else {
      model[key] = apiData[key];
    }
  });
  return model;
}

// 根据类获取对应配置的辅助函数
function getModelConfig(modelClass: any) {
  if (modelClass === Product) return ProductTypeConfig;
  if (modelClass === Price) return {}; // 可扩展Price的配置
  return {};
}

3. 临时实例推断(局限性较大)

可以创建类的临时实例,通过实例属性值推断类型,但这种方式对引用类型的判断有限(比如只能知道是数组,无法直接获取元素的模型类型):

export class Price {}

export class Product {
  id: number;
  title: string;
  prices: Price[];

  constructor() {
    this.id = null;
    this.title = null;
    this.prices = [];
  }
}

function getPropertyTypesFromInstance(modelClass: any): Record<string, string> {
  const tempInstance = new modelClass();
  const types: Record<string, string> = {};
  
  Object.keys(tempInstance).forEach(key => {
    const value = tempInstance[key];
    if (Array.isArray(value)) {
      types[key] = 'array';
    } else {
      types[key] = typeof value;
    }
  });
  
  return types;
}

// 使用示例
const types = getPropertyTypesFromInstance(Product);
console.log(types);
// 输出:{ id: 'object', title: 'object', prices: 'array' }
// 注意:初始值为null的属性,typeof会返回object,存在局限性

总结:装饰器+元数据的方案能准确获取属性类型(包括子模型数组的类型),是最推荐的方案;手动配置适合小型项目;临时实例方案仅能作为补充,局限性较大。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 19:30:18