如何从被装饰器标记的类中获取追加对应Id字段的修改后类型
实现方案说明
首先明确:纯TypeScript类型体操无法自动识别被属性装饰器标记的字段——TS类型系统不会自动追踪属性装饰器给类带来的类型标记,必须通过装饰器联动类型声明、或者配合泛型手动指定属性范围,才能实现你要的效果,下面是两种可落地的实现:
方案1:轻量泛型实现(无额外装饰器改造,成本最低)
不需要改原有装饰器逻辑,直接写一个工具泛型,手动传入需要追加Id字段的属性名即可,类型推导100%准确:
// 工具泛型定义 type AppendRelationId<T, K extends keyof T> = Omit<T, `${K & string}Id`> & { [P in K as `${P & string}Id`]: string } // 原有类定义保持不变 class Animal {} class Person { @OneToOne() pet: Animal; } // 生成修改后的类型 type ModifiedPerson = AppendRelationId<Person, 'pet'> // 推导结果等价于 { pet: Animal; petId: string }
如果有多个关系字段,直接传联合类型就行,比如AppendRelationId<Person, 'pet' | 'car'>会自动生成petId、carId两个字符串字段。
方案2:装饰器联动自动收集(无需手动传属性名)
如果要做到完全自动识别被@OneToOne()标记的属性,需要改造装饰器逻辑,利用TS声明合并+类装饰器做类型收集,步骤如下:
- 定义唯一标记常量,用来存装饰器标记的属性元数据和类型标记
- 改造
@OneToOne()装饰器,在运行时存储被标记的属性名 - 增加类装饰器,运行时扫描所有标记属性、追加对应逻辑,同时在类型层面返回带Id字段的新类型
代码示例:
import 'reflect-metadata' const ONE_TO_ONE_META_KEY = Symbol('OneToOneMeta') // OneToOne装饰器保持原有运行时逻辑,只额外存元数据 function OneToOne() { return function (target: any, propertyKey: string) { // 原有装饰器逻辑不变,比如定义关联关系 const existingRelations = Reflect.getMetadata(ONE_TO_ONE_META_KEY, target.constructor) || [] existingRelations.push(propertyKey) Reflect.defineMetadata(ONE_TO_ONE_META_KEY, existingRelations, target.constructor) } } // 辅助类型:递归拼接Id字段 type BuildEntityType<T, Keys extends string> = Omit<T, `${Keys}Id`> & { [K in Keys as `${K}Id`]: string } // 类装饰器,用来做类型转换和运行时逻辑增强 function Entity() { return function <T extends new (...args: any[]) => any>(target: T) { // 运行时逻辑:读取标记的属性,可自行追加字段访问器、序列化逻辑等 const relations: string[] = Reflect.getMetadata(ONE_TO_ONE_META_KEY, target) || [] relations.forEach(relKey => { const idKey = `${relKey}Id` // 这里可以加自定义运行时实现,比如给Id字段加get/set、默认值等 }) return target as T & (new (...args: ConstructorParameters<T>) => BuildEntityType<InstanceType<T>, typeof relations[number]>) } } // 使用时用Entity装饰类即可 class Animal {} const Person = Entity()(class Person { @OneToOne() pet: Animal }) type ModifiedPerson = InstanceType<typeof Person> // 自动推导为 { pet: Animal; petId: string },不需要手动传属性名
注意:这个方案需要在
tsconfig.json中开启experimentalDecorators: true和emitDecoratorMetadata: true配置。
内容的提问来源于stack exchange,提问作者SnowSuno
相关产品推荐
相关产品推荐

