如何获取TypeScript中未初始化的类属性?
问题描述
我想创建一个能接收任意类、接口或对象类型的通用映射器,但传入的多数类都没有初始化属性,示例如下:
export class LoginDTO { @IsStringAndNotEmpty() email: string; @IsStringAndNotEmpty() password: string; }
当我执行下面的genericMapper函数时:
/** * 通用映射器,可映射到任意对象、类型、接口或类 * @param source 要映射属性的源对象 * @param target 提供要映射的属性键的目标对象/类 * @returns 目标类型的部分实例 */ export function genericMapper<T>(source: any, target): T { const result = {} as T; try { const map = isClass(target) ? new target() : target; for (const key in map) { result[key] = source[key]; } } catch { } return result; }
这个函数无法正常工作,因为TypeScript中未初始化的属性默认不可枚举,但我没办法初始化项目中所有DTO的属性。我试过用Reflect.ownKeys(target.prototype)但没成功,请问怎么获取类的未初始化属性?
解决方案
1. 利用TypeScript元数据(结合装饰器)
你的DTO已经使用了装饰器,只要在tsconfig.json中开启emitDecoratorMetadata和experimentalDecorators编译选项,TypeScript会自动为类属性添加元数据,就能通过Reflect API获取未初始化的属性:
首先配置tsconfig.json:
{ "compilerOptions": { "emitDecoratorMetadata": true, "experimentalDecorators": true } }
修改映射器函数:
import 'reflect-metadata'; // 判断是否为类的辅助函数 function isClass(target: any): boolean { return typeof target === 'function' && target.prototype !== undefined; } export function genericMapper<T>(source: any, target: new () => T): Partial<T> { const result = {} as Partial<T>; try { if (isClass(target)) { // 获取类原型上的所有自有属性,排除构造函数 const propertyKeys = Object.getOwnPropertyNames(target.prototype) .filter(key => key !== 'constructor'); // 遍历属性键,从源对象取值 for (const key of propertyKeys) { if (source.hasOwnProperty(key)) { result[key as keyof T] = source[key]; } } } else { // 处理普通对象的情况 for (const key in target) { if (source.hasOwnProperty(key)) { result[key as keyof T] = source[key]; } } } } catch (err) { console.error('映射失败:', err); } return result; }
2. 手动维护静态属性列表(备选方案)
如果不想依赖元数据编译选项,可以给每个DTO添加静态属性声明所有字段:
export class LoginDTO { @IsStringAndNotEmpty() email: string; @IsStringAndNotEmpty() password: string; static readonly fields = ['email', 'password'] as const; }
然后在映射器中读取这个静态属性:
function isClass(target: any): boolean { return typeof target === 'function' && target.prototype !== undefined; } export function genericMapper<T>(source: any, target: new () => T & { fields?: readonly (keyof T)[] }): Partial<T> { const result = {} as Partial<T>; try { if (isClass(target)) { // 优先使用静态字段列表, fallback到原型属性 const propertyKeys = target.fields || Object.getOwnPropertyNames(target.prototype).filter(key => key !== 'constructor'); for (const key of propertyKeys) { if (source.hasOwnProperty(key)) { result[key] = source[key]; } } } else { for (const key in target) { if (source.hasOwnProperty(key)) { result[key as keyof T] = source[key]; } } } } catch (err) { console.error('映射失败:', err); } return result; }
3. 使用第三方工具简化(可选)
如果项目允许引入第三方库,class-transformer专门处理TypeScript类与对象的转换,完美适配带装饰器的DTO:
import { plainToInstance } from 'class-transformer'; // 直接调用即可自动映射所有DTO属性 const loginDto = plainToInstance(LoginDTO, source);
内容的提问来源于stack exchange,提问作者Miguel
相关产品推荐
相关产品推荐

