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

如何从被装饰器标记的类中获取追加对应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声明合并+类装饰器做类型收集,步骤如下:

  1. 定义唯一标记常量,用来存装饰器标记的属性元数据和类型标记
  2. 改造@OneToOne()装饰器,在运行时存储被标记的属性名
  3. 增加类装饰器,运行时扫描所有标记属性、追加对应逻辑,同时在类型层面返回带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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:21:22