2015-11-25 10 views
8

Sto costruendo una semplice mappatura tra struttura dati frontend/backend. Per fare questo ho creato un decoratore che è simile al seguente:Assegnazione di proprietà a non prototipo con decoratori

function ApiField(
    apiKey: string, 
    setFn: (any) => any = (ret) => ret, 
    getFn: (any) => any = (ret) => ret 
) { 
    return function (target: AbstractModel, propertyKey: string) { 
     target.apiFieldsBag = target.apiFieldsBag || {}; 
     _.assign(
      target.apiFieldsBag, 
      { 
       [propertyKey]: { 
        apiKey: apiKey, 
        setFn: setFn, 
        getFn: getFn 
       } 
      } 
     ); 
    }; 
} 

E questo è come lo uso:

class AbstractCar { 
    @ApiField('id') 
    public id: string = undefined; 
} 

class BMW extends AbstractCar { 
    @ApiField('cylinders') 
    public cylinderCount: number; 
} 

class VW extends AbstractCar { 
    @ApiField('yearCompanyFounded') 
    public yearEstablished: number; 
} 

Il problema che sto vedendo è che invece di l'oggetto reale di essere passato al decoratore è sempre il suo prototipo:

__decorate([ 
    ApiField('yearCompanyFounded') 
], VW.prototype, "yearEstablished", void 0); 

il che significa che, come sto assegnando roba per l'istanza nella decoratore, è sempre attaccato al prototipo a sua volta significa che le proprietà che voglio definire solo l'istanza VW sono disponibili anche su AbstractCar e sulla classe BMW (in questo esempio, questo sarebbe yearEstablished). Ciò rende impossibile avere due proprietà con lo stesso nome ma campi API diversi in due classi diverse.

C'è un modo per aggirare questo comportamento?

+0

ECMAScript 7 ... !!!! – Arjun

+0

Come parte, non è necessario 'target.apiFieldsBag =' davanti a '_.assign ('. Hai anche la possibilità di usare ES6 'Object.assign()' invece di '_.assign() ' –

+0

Sì, l'ho capito mentre guardavo di nuovo la domanda.Grazie per l'avviso comunque :) –

risposta

4

questo momento, tutte le tre classi stanno aggiungendo oggetti da lo stesso oggetto. La chiave per risolvere questo problema è clone l'oggetto su target.data in modo che ogni classe utilizzi un oggetto diverso anziché tutti che si riferiscano allo stesso oggetto.

Ecco un esempio più semplice che dimostra un modo di fare questo:

function ApiField(str: string) { 
    return function (target: any, propertyKey: string) { 
     // I tested with Object.assign, but it should work with _.assign the same way 
     target.data = _.assign({}, target.data, { 
      [propertyKey]: str 
     }); 
    }; 
} 

class AbstractCar { 
    @ApiField("car") 
    public carID; 
} 

class BMW extends AbstractCar { 
    @ApiField("bmw") 
    public bmwID; 
} 

class VW extends AbstractCar { 
    @ApiField("vw") 
    public vwID; 
} 

AbstractCar.prototype.data; // Object {carID: "car"} 
BMW.prototype.data;   // Object {carID: "car", bmwID: "bmw"} 
VW.prototype.data;   // Object {carID: "car", vwID: "vw"} 
+0

Il fatto è che voglio che la Classe BMW erediti i dati dalla AbstractCar –

+0

@justNik ah, ok. Ho aggiornato la mia risposta con qualcosa che funzioni per quello. –

+0

Dolce, grazie! È divertente come una cosa così complicata si riduca a un problema javascript così comune. Parla della mancanza della foresta per gli alberi –

4

Il problema è che public all'interno di una classe non è JavaScript standard, è solo qualcosa che fa TypeScript. Pertanto, devi stare attento, perché tutto ciò che fai potrebbe rompersi in futuro.

Una possibilità è quella di utilizzare Object.assign() per aggiungere proprietà dell'istanza (IINM, apiFieldsBag dovrebbero essere trasferiti dall'oggetto creato dall'oggetto letterale this):

class AbstractCar { 
    constructor() { 
     Object.assign(this, { 
      @ApiField('id') 
      id: undefined, 
     }); 
    } 
} 
+0

In realtà sto usando dattiloscritto, quindi usare' public' dovrebbe andare bene :) Ma ho intenzione di provare la tua soluzione –

+1

@justNik So che 'public' è TypeScript. Il problema con 'public foo = x' è che non esiste come entità reale (ad esempio una proprietà), è compilato per un compito nel costruttore. Pertanto, non è chiaro cosa dovrebbe essere decorato, perché, ATM, solo le classi e le proprietà possono essere decorate. –

+0

@justNik Ho appena visto il codice in cui è compilato un 'public' decorato e TypeScript effettivamente decora una proprietà del prototipo. Questo è un bug, IMO (dovrebbe decorare una proprietà di 'this' nel costruttore) - potresti voler inserire un bug report. –

Problemi correlati