我想编写一个 sanitizer 装饰器,可以将其放在所有用户输入字符串字段上。这只是将标准 .set(newValue)
替换为 .set( sanitize(newValue) )
。但是我发现下面的代码仅适用于一个实例。同一类的第二个实例最终共享 currentValue。进一步阅读后,这实际上是预期的,但我无法弄清楚如何针对每个实例进行它。
import "reflect-metadata";
export const Sanitize = () => {
return (target: any, propertyKey: string | symbol) => {
let currentValue: any = sanitiseString(options, `${target[propertyKey] || ''}`);
Reflect.deleteProperty(target, propertyKey);
Reflect.defineProperty(target, propertyKey, {
get: () => currentValue,
set: (newValue: string) => {
currentValue = sanitiseString(newValue);
},
});
}
}
编辑1: 最小可重现示例:
import "reflect-metadata";
const sanitiseString = (valToSanitise: string) => {
// do some stuff, return clean value
return valToSanitise;
}
const Sanitize = () => {
return (target: any, propertyKey: string | symbol) => {
let currentValue: any = sanitiseString(`${target[propertyKey] || ''}`);
Reflect.deleteProperty(target, propertyKey);
Reflect.defineProperty(target, propertyKey, {
get: () => currentValue,
set: (newValue: string) => {
currentValue = sanitiseString(newValue);
},
});
}
}
class UserInput {
constructor(propOne: string, propTwo: string, propThree: number) {
this.propOne = propOne;
this.propTwo = propTwo;
this.propThree = propThree;
}
@Sanitize() propOne: string
@Sanitize() propTwo: string
propThree: number
}
const inputOne = new UserInput('input 1, prop 1', 'input 1, prop 2', 1)
const inputTwo = new UserInput('input 2, prop 1', 'input 2, prop 2', 2)
console.log(inputOne)
console.log(inputTwo)
// expected output:
// [LOG]: UserInput: {
// "propOne": "input 1, prop 1",
// "propTwo": "input 1, prop 2",
// "propThree": 1
// }
// [LOG]: UserInput: {
// "propOne": "input 2, prop 1",
// "propTwo": "input 2, prop 2",
// "propThree": 2
// }
//
// actual output:
//
// [LOG]: UserInput: {
// "propThree": 1
// }
// [LOG]: UserInput: {
// "propThree": 2
// }
// When you remove @Sanitize() the fields appear in console.log. When you add @Sanitize() the fields disappear.
// Further, forcing console.log(inputOne.propOne) returns [LOG]: "input 2, prop 1"
// indicating that the property is being written for the class proto and not per instance
console.log(inputOne.propOne)
最佳答案
这里的主要问题是,每个修饰类属性声明都会调用一次 Sanitize()
,因此对于任何给定的类属性,都只有一个 currentValue
。这意味着该类的两个实例将共享相同的currentValue
。如果您想为每个类实例的每个装饰类属性存储一个值,那么您需要访问类实例,并且您必须将值存储在这些实例中(通过属性键不是'不会干扰任何其他属性),或者在某些其键是这些实例的映射中。下面我将展示如何在类实例中存储值,为了避免担心属性名称冲突,我将使用 the Symbol
function 的 symbol
输出。 ,保证是唯一的。
另请注意,Sanitize()
会作为 target
参数传递类原型(prototype),因此您对 target 执行的任何操作
将影响原型(prototype)而不是类的任何实例。当您编写 target[propertyKey]
时,您正在类原型(prototype)中查找属性,并且 string
值属性几乎肯定不会在原型(prototype)中设置。所以这可能没有必要或没有用,我们应该摆脱它。
因此,如果您只能直接访问类原型(prototype),那么如何对类实例执行任何操作呢?那么,要执行此操作,您应该使用 get
method 的 this
上下文。和 set
method传递给 defineProperty()
的访问器属性描述符。这意味着 get
和 set
需要是 methods或至少function
expressions ,并且不arrow function expressions没有明显的 this
上下文。
好的,解释得够多了,这是代码:
const Sanitize = () => {
return (target: any, propertyKey: string | symbol) => {
const privatePropKey = Symbol();
Reflect.defineProperty(target, propertyKey, {
get(this: any) {
return this[privatePropKey]
},
set(this: any, newValue: string) {
this[privatePropKey] = sanitiseString(newValue);
},
});
}
}
让我们确保它按您的预期工作。让我们让 sanitiseString
实际做一些事情:
const sanitiseString = (valToSanitise: string) => {
return valToSanitise+"!";
}
现在我们类:
class UserInput {
constructor(propOne: string, propTwo: string, propThree: number) {
this.propOne = propOne;
this.propTwo = propTwo;
this.propThree = propThree;
}
@Sanitize() propOne: string
@Sanitize() propTwo: string
propThree: number
}
最后让我们看看它是否有效:
const inputOne = new UserInput('input 1, prop 1', 'input 1, prop 2', 1)
const inputTwo = new UserInput('input 2, prop 1', 'input 2, prop 2', 2)
console.log(inputOne.propOne, inputOne.propTwo, inputOne.propThree)
console.log(inputTwo.propOne, inputTwo.propTwo, inputTwo.propThree);
// GOOD OUTPUT
// [LOG]: "input 1, prop 1!", "input 1, prop 2!", 1
// [LOG]: "input 2, prop 1!", "input 2, prop 2!", 2
看起来不错。 UserInput
的每个实例都有自己经过清理的 propOne
和 propTwo
属性。
关于node.js - Typescript 属性装饰器可以修改实例成员而不是整个类吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71314708/