简体   繁体   English

如何创建 Typescript 装饰器,根据以“_”开头的属性名称将所有 class 属性设置为不可枚举?

[英]How to create Typescript decorator that sets all class properties as non-enumerable based on property name starting with “_”?

How do you get a Typescript class decorator to call a method that sets any property or method starting with an "_" as non-enumerable property (essentially making it private for serialization purposes)?如何获得 Typescript class 装饰器来调用将任何以“_”开头的属性或方法设置为不可枚举属性的方法(本质上将其设为私有以用于序列化目的)?

Imagine there's a class that's a base class which every other class in my application will extend from:想象一下,有一个 class 是基础 class ,我的应用程序中的所有其他 class 将从:

class Base {
     constructor(data: any){
        Object.assign(this, data)
     }

     _setNonEnumerableProperties(){
        Object.keys(this).forEach(key => {
            if(key[0] === '_')
                Object.defineProperty(this, key, { enumerable: false })
        })
     }
}

And then I have a User class like this:然后我有一个像这样的用户 class :

@nonEnumerablePrivateMembers
class User extends Base {
     public name: string
     public email: string
     private _active: boolean
}

And I create and JSON stringify a User instance like this:我创建并 JSON 像这样对 User 实例进行字符串化:

const user = new User({name: 'John', email: 'john@example.com', _active: false})
const jsonUser = JSON.stringify(user)
console.log(jsonUser)

I expect the output to be this:我希望 output 是这样的:

{ "name": "John", "email": "john@example.com" }

and not this:而不是这个:

{ "name": "John", "email": "john@example.com", "_active": false }

Note that it doesn't include the _active property.请注意,它不包括_active属性。

I need to know how to write nonEnumerablePrivateMembers decorator that would call the _setNonEnumerableProperties method on the Base class upon a new instance of the extended class.我需要知道如何编写nonEnumerablePrivateMembers装饰器,它会在扩展 class 的新实例上调用 Base class 上的_setNonEnumerableProperties方法。

Any suggestions?有什么建议么?

Overriding the constructor appears to work as intended:覆盖构造函数似乎按预期工作:

interface Class<T> {
  new(...args: any[]): T
}

function nonEnumerablePrivateMembers<T extends Class<Base>>(cls: T): T {
  return class extends cls {
    constructor(...args: any[]) {
      super(...args);
      this._setNonEnumerableProperties();
    }
  }
}

Playground 操场

I'm sure someone with better TypeScript skills can modify this to be a bit cleaner, but based on the TypeScript decorator docs you can have something like this:我敢肯定,拥有更好的 TypeScript 技能的人可以将其修改为更简洁,但基于TypeScript 装饰器文档,您可以拥有以下内容:

function fixEnumerables<T extends { new (...args: any[]): {} }>(
  constructor: T
) {
  return class extends constructor {
    constructor(...data: any[]) {
      super(data);
      Object.assign(this, data[0]);
      Object.keys(this).forEach(key => {
        if (key[0] === "_") {
          console.log(`Freezing "${key}"`);
          Object.defineProperty(this, key, { enumerable: false });
        }
      });
    }
  };
}

@fixEnumerables
class User {
  public name: string;
  public email: string;
  private _active: boolean;
  constructor(data: any) {}
}

const x = new User({ name: "Sam", email: "email@example.com", _active: false });
console.log(JSON.stringify(x));

// Output
// Freezing "_active"
// {"name":"Sam", "email": "email@example.com"}

Blitz闪电战

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

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