I am building a simple mapping between frontend/backend data structure. In order to do that I've created a decorator that looks like the following:
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
}
}
);
};
}
And this is how I use it:
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;
}
The issue that I'm seeing is that instead of the actual object being passed to the decorator it's always its prototype:
__decorate([
ApiField('yearCompanyFounded')
], VW.prototype, "yearEstablished", void 0);
Which means that as I am assigning stuff to the instance in the decorator, it is always attached to the prototype which in turn means that properties I want to be defined only the VW
instance are also available on the AbstractCar
and the BMW
class (in this example this would be yearEstablished
). This makes it impossible to have two properties with the same name but different API fields in two different classes.
Is there any way to circumvent this behaviour?
Right now, all three classes are adding properties to the same object. The key to fix this is to clone the object on target.data
so that each class is using a different object instead of all of them referring to the same object.
Here's a simpler example that demonstrates one way of doing this:
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"}