繁体   English   中英

typescript 属性装饰器可以修改实例成员而不是整个 class 吗?

[英]Can typescript property decorators modify instance members instead of the entire class?

我想写一个消毒剂装饰器,我可以把它放在所有用户输入的字符串字段上。 这只是将标准的.set(newValue)替换为.set( sanitize(newValue) ) 但是我发现下面的代码只适用于一个实例。 相同 class 的第二个实例最终共享 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()在每个修饰的 class 属性声明中被调用一次,因此对于任何给定的 class 属性,只有一个currentValue 这意味着 class 的两个实例将共享相同的currentValue 如果你想为每个class 实例的每个修饰的 class 属性存储一个值,那么你需要访问 class 个实例,并且你必须将值存储在这些实例中(通过不会干扰任何其他实例的属性键) properties),或者在一些 map 中,其键是那些实例。 在下文中,我将展示如何将值存储在 class 实例中,并且为了避免担心属性名称冲突,我将使用symbol function Symbol output ,保证唯一。

另请注意,将class 原型作为target参数传递给Sanitize() ,因此您对target执行的任何操作都会影响原型,而不是 class 的任何实例。当您编写target[propertyKey]时,您正在查找属性class 原型和string值属性几乎肯定不会在原型中设置。 所以这可能没有必要或没有用,我们应该摆脱它。

因此,如果您只能直接访问 class 原型,您如何对 class 实例执行任何操作? 那么,为此,您应该使用传递给defineProperty()的访问器属性描述符的get方法set方法this上下文。 这意味着getset需要是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:

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的每个实例都有自己经过净化的propOnepropTwo属性。

游乐场代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM