2016-06-09 4 views
1

Ich muss ein Objekt zu json in eckigen 2.0.0-rc1 serialisieren, als ich festgestellt, dass Typescript private ist überhaupt nicht privat, und Get Set-Eigenschaft werden nicht ausgegeben durch JSON.stringify.Typoskript: Dekorateure verhalten sich auf eckigen Projekt und Typoskript Spielplatz anders

Also habe ich die Klasse dekorieren aus:

//method decorator 
function enumerable(value: boolean) { 
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { 
     descriptor.enumerable = value; 
    }; 
} 
//property decorator 
function exclude(target: any, propertyKey: string): any { 
    return { enumerable: false }; 
} 
class MyClass { 
    test: string = "test"; 
    @exclude 
    testExclude: string = "should be excluded"; 
    @enumerable(true) 
    get enumerated(): string { 
     return "yes"; 
    } 
    @enumerable(false) 
    get nonEnumerated(): string { 
     return "non enumerable" 
    } 
} 

let x = new MyClass(); 
//1st 
console.log(JSON.stringify(x)); 
//2nd 
console.log(JSON.stringify(x, Object.keys(MyClass.prototype))); 
//3rd 
console.log(JSON.stringify(x, Object.keys(x).concat(Object.keys(MyClass.prototype))));//test 3 

auf Typescript playground gibt diese

{"test":"test"} 
{"enumerated":"yes"} 
{"test":"test","enumerated":"yes"} 

aber auf meinem Projekt (Winkel 2.0.0-rc1), das gibt

{"test":"test","testExclude":"should be excluded"} 
{"enumerated":"yes"} 
{"test":"test","testExclude":"should be excluded","enumerated":"yes"} 

Was ich wirklich will, ist die Ausgabe # 3 vom Spielplatz.

Nach einem Blick auf die transpiled Code nehmen, der einzige Unterschied ist, widerspiegeln-Metadaten-Code:

//snip ... 

    __decorate([ 
     exclude, 
     __metadata('design:type', String) 
    ], MyClass.prototype, "testExclude", void 0); 
    __decorate([ 
     enumerable(true), 
     __metadata('design:type', String) 
    ], MyClass.prototype, "enumerated", null); 
    __decorate([ 
     enumerable(false), 
     __metadata('design:type', String) 
    ], MyClass.prototype, "nonEnumerated", null); 
    return MyClass; 
}()); 

Nichts davon __metadata Linien in Spielplatz.

Was passiert hier? Und wie kann ich das Ergebnis des Spielplatzes # 3 für mein Projekt erreichen?

Antwort

1

Fixed (oder möglicherweise nur ein Workaround).

Beachten Sie, dass Reflect-Metadaten in Playground nicht verfügbar sind. Property-Decorators können ein Objekt, das dem Deskriptor zugewiesen (ORed) werden soll, zurückgeben, um sein Verhalten zu ändern. In eckigen Umgebungen werden Reflect-Metadaten (speziell Reflect.decorate()) verwendet, um Dinge zu dekorieren.

Nach dem Lesen auf reflect-metadata doc und this, gibt es anscheinend keine Möglichkeit, PropertyDescriptor auf Property Decorator zu ändern, da es an den Konstruktor anstelle des Prototyps gebunden ist. Eine Lösung (Problemumgehung) wäre, die Eigenschaft mit dem neuen Deskriptor neu zu erstellen.

function include(value: boolean) { 
    return function (target: any, propertyKey: string): any { 
     // Buffer the value 
     var _val = target[propertyKey]; 
     // Delete property. 
     if (delete target[propertyKey]) { 
      // Create new property with getter and setter 
      Object.defineProperty(target, propertyKey, { 
       get:() => _val, 
       set: (newVal) => _val = newVal, 
       enumerable: value, 
       configurable: true 
      }); 
     } 
    } 
} 

die Fabrik wird nur benötigt, so konnte ich @include(false) statt @exclude verwenden.

Einziger Nachteil ist die Tatsache, dass die Eigenschaft jetzt an den Prototyp gebunden ist, daher normal JSON.stringify(instance) würde es nicht serialisieren.

In diesem Sinne können wir eine generische Dekorateur verwendbar sowohl in der Schaden- und Verfahren, machen weiter als solche:

//method decorator 
function excludeMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) { 
    descriptor.enumerable = false; 
    return descriptor; 
}; 
//property decorator 
function excludeProperty(target: any, propertyKey: string): any { 
    // Buffer the value 
    var _val = target[propertyKey]; 
    // Delete property. 
    if (delete target[propertyKey]) { 
     // Create new property with getter and setter 
     Object.defineProperty(target, propertyKey, { 
      get:() => _val, 
      set: (newVal) => _val = newVal, 
      enumerable: false, 
      configurable: true 
     }); 
    } 
} 
function exclude(...args : any[]) { 
    switch(args.length) { 
     case 2: 
      return excludeProperty.apply(this, args); 
     case 3: 
      if (typeof args[2] !== "number") 
       return excludeMethod.apply(this, args); 
     default: 
      throw new Error("Decorators are not valid here!"); 
    } 
} 

so jetzt können wir sie als solche verwendet werden:

class MyClass { 
    test: string = "test"; 
    @exclude 
    testExclude: string = "should be excluded"; 
    get enumerated(): string { 
     return "yes"; 
    } 
    @exclude 
    get nonEnumerated(): string { 
     return "non enumerable" 
    } 
    constructor() {} 
} 

let x = new MyClass(); 
//to serialize, we have to whitelist the instance and its prototype prop keys 
console.log(JSON.stringify(x, Object.keys(x).concat(Object.keys(MyClass.prototype)))); 

weit So Ich habe keinen saubereren Weg gefunden, dies zu tun.

+0

Perfekt funktioniert. speicherte meinen Tag .. –

+0

@KaranPatel vor meiner späteren Antwort gewarnt wird, arbeitet dieser Dekorateur nicht auf verschachtelten Objekten. – Evan

+0

Dank Evan .. Ich habe diese aktuelle Antwort nicht unter Antwort genannt. aber eine Sache, die Sie tun können, ist, können Sie unter Antwort entfernen, wenn es nicht richtig funktioniert. –

0

in den Kaninchenbau fiel ich ...

so aus irgendeinem Grund zu JSON Hinzufügen Weiße Liste.stringify irgendwie machte es nicht rekursiv verschachtelte Objekte serialisiert:

class a { 
    p1 = 1; 
    p2 = 2; 
} 
class b { 
    m1 = new a(); 
    m2 = "test"; 
    m3 = new Array<a>(); 
} 
let i = new b(); 
i.m3.push(new a()); 
i.m3.push(new a()); 

JSON.stringify(i); 
// properly gives 
// {"m1":{"p1":1,"p2":2},"m2":"test","m3":[{"p1":1,"p2":2},{"p1":1,"p2":2}]} 

JSON.stringify(i, Object.keys(i).concat(Object.keys(Object.getPrototypeOf(i)))); 
// nested class a doesn't get serialized 
// {"m1":{},"m2":"test","m3":[{},{}]} 

So einfach diese setzen da draußen, wenn Sie wie ich sind und wollen private Variablen in TS verstecken und eine Nur-Lese-Fassade Eigenschaft geben:

deklariert es als ein einfaches Objekt Mitglied, dann seinen PropertyDescriptor im Konstruktor als solche ändern:

//Don't do this 
class a { 
    private _prop; 
    get prop() { return _prop; } 
} 

//do this instead 
class a { 
    prop; //just define your public-facing property 
    constructor() { 
     let _prop; //internal variable here 
     Object.defineProperty(this, "prop", { //now we modify the existing prop, 
      get:() => _prop, //closure to outside variable 
      //(no set here, it's readonly after all) 
      enumerable: true, //make sure it's visible 
      configurable: false //close up access 
     }); 
    } 
} 

wir jetzt einfach JSON.stringify(instance) nutzen können. Der einzige Nachteil ist, wenn Sie komplizierte Getter/Setter haben, bedenken Sie, dass dies in jeder Instanz/neu aufgerufen wird.

mit diesem Muster und @exclude Decorators oben, löst ziemlich viel meinen Anwendungsfall. Hoffe, das hilft jemandem ..

Verwandte Themen