具有复制属性的类的方法上的元数据在更改源类的元数据时发生更改-Typescript

具有复制属性的类的方法上的元数据在更改源类的元数据时发生更改-Typescript,typescript,reflection,decorator,nestjs,factory,Typescript,Reflection,Decorator,Nestjs,Factory,抱歉,标题太冗长了。我有一个类MutateMe被一个修饰师decorator传递到一个名为FilterFactory的工厂 export const Decorator=(选项?:DecoratorOptions)=>(目标:T)=>{ 新过滤器工厂(目标、选项); } 在这个工厂中,我将方法复制到target类并设置其元数据 导出类过滤器工厂{ 构造函数(受保护的目标:任意,选项:装饰选项){ //获取对类的引用,我想从中复制具有自己元数据的方法 常量路由控制器=过滤器控制器; //类本身包

抱歉,标题太冗长了。我有一个类
MutateMe
被一个修饰师
decorator
传递到一个名为
FilterFactory
的工厂

export const Decorator=(选项?:DecoratorOptions)=>(目标:T)=>{
新过滤器工厂(目标、选项);
}
在这个工厂中,我将方法复制到
target
类并设置其元数据

导出类过滤器工厂{
构造函数(受保护的目标:任意,选项:装饰选项){
//获取对类的引用,我想从中复制具有自己元数据的方法
常量路由控制器=过滤器控制器;
//类本身包含一个前缀,该前缀必须在其所有成员方法的元数据前面。
const prefixRoute=getControllerPrefix(路由控制器);
log(“对于每个键(成员名称)”)
Reflect.ownKeys(routecontroller.prototype).forEach(
(财产)=>{
//忽略基本类方法
if(!['constructor','toString','length'].includes(property.toString())){
//将方法复制到“目标”`
Object.defineProperty(
target.prototype,
财产,,
Object.getOwnPropertyDescriptor(
RouteController.prototype,
财产
)
)
//将类元数据“filter”前置到每个路由方法的元数据
patchRoutes(target.prototype[property],前缀为route)
//注:原型属性分配的替代方案(也不起作用)
//target.prototype[property]=RouteController.prototype[property]
log(Reflect.getownmatadata(PATH\u元数据,target.prototype[property]))
}
})
}
}
patchRoutes
功能如下:

const patchRoutes=(patchee:any,patches:(T|T[]|((…args:P[])=>(T|T[]),…args:P[])=>{
const existingPath=Reflect.getownmatadata(路径\元数据,补丁)
if(patches instanceof Function){patches=patches(…args)}
如果(!Array.isArray(patches))patches=[patches]
Reflect.defineMetadata(路径\元数据,(existingPath==“/”?[…补丁]:[…补丁,existingPath])。加入(“/”,补丁)
const createResetCallback=(resetValue,resetTarget)=>()=>
Reflect.defineMetadata(路径\元数据、重置值、重置目标)
返回createResetCallback(现有路径,修补程序)
}
它返回一个
reset
回调以重置修补的元数据

现在,当我用这个装饰器装饰多个类时,我可以看到修补的重复

例如,修补一次会给我
foo/filter/…
,第二次调用会给我
bar/filter/filter/…

我想看看是否是复制方法不当的问题,因此,我尝试修补基类,复制修补过的方法并重置基类的元数据:

const propertyResetCb=patchRoutes(routecontroller.prototype[property],前缀route)
...
//现在将属性指定给目标
...
//调用重置回调函数
propertyResetCb()
然而,这似乎重置了我制作的所有装饰器的属性

这让我相信它使用了一个单一的原型引用来复制方法。我希望可以免费复制它们(如果愿意,可以克隆),这样我就可以独立设置它们的元数据

另外,如果我不必修改
patchRoutes
来考虑复制的因素,我更愿意这样做,因为最后,我想分别对它们各自的元数据进行更多的修改

谢谢:)

更新 @Mirco S的回答解决了我的问题。还需要添加一点元数据复制逻辑

Reflect.defineMetadata(
路径u元数据,
Reflect.getOwnMetadata(路径\元数据,oldPropertyDescriptor.value),
newPropertyDescriptor.value
)

这可能是因为属性描述符中的属性值始终是相同的函数。没有适用于所有类型对象的通用深度复制函数,但对于函数,您可以尝试以下操作:

// clone the propertyDescriptor to not temper with the original.
const newPropertyDescriptor = {...Object.getOwnPropertyDescriptor(
  routesController.prototype,
  property
)}

if(typeof newPropertyDescriptor.value === "function") {
  const routesControllerFunction = newPropertyDescriptor.value;
  // wrap the original function so that Reflect.defineMetadata gets applied to the
  // newly created function instead of to the prototype function of FilterController
  newPropertyDescriptor.value = (...args: any[]) => routesControllerFunction(...args);
}

Object.defineProperty(
  target.prototype,
  property,
  newPropertyDescriptor
)
如果需要克隆多个函数,则必须添加更多案例并正确克隆它们。但是要小心,如果您复制这样的函数,您可能需要调整绑定,并且可能需要添加逻辑以在修饰类中维护适当的
this


编辑:关于装饰者的一点旁注。我自己也喜欢装饰师,但经过几年的漫长岁月,他们仍处于第二阶段。typescript中装饰器的当前实现受2014年遗留提案的启发,该提案不再符合标准。最新的提案是WIP,据我所知,目前还没有关于WIP的透明提案(2021年3月)。在最新的提案中有一些突破性的变化,因此请注意,您可能需要在将来更新您的装饰师。尽管如此,在最新的提案中,您可以使用遗留装饰器完成的所有工作都应该是可行的。我们还有可能得到另一个提案……

瞧!这正是我想要的。看起来
getOwnPropertyDescriptor()
只是返回了对描述符的引用,而不是副本(可能我对描述符的工作方式不太熟悉)。我不得不添加这一点,以使其适用于我的用例编辑:看起来注释不喜欢代码块。我已经用要求的内容更新了我的问题。