reflect-metadata
reflect-metadata copied to clipboard
Decorated class inheritance issue
When multiple instances extends from the same base class that has decorated properties. Children decorators 'pollutes' parent metadata. They were also attached to parent prototype.
Example:
following decorators use reflect-metadata
to 'define' and 'get' metadata.
class Base {
@SomeDecorator
prop
}
class A extends Base {
@SomeDecorator
propA
}
class B extends Base {
@SomeDecorator
propB
}
This will result class B
has three metadata, prop
, propA
and propB
What does SomeDecorator
look like? If it's anything like the decorator mentioned in #53, check https://github.com/rbuckton/reflect-metadata/issues/53#issuecomment-274906502
I am also seeing this. Although, I am using decorators on constructor parameters rather than as class properties. I imagine the code path is roughly the same though.
class AbstractComponent {
constructor (@Inject('form') @Host() formController: IFormController) { ... }
}
class ComponentA extends AbstractComponent { ... } // no constructor
class ComponentB {
constructor (@Inject('form') @Host() formController: IFormController,
@Inject('$http') $http: IHttpService) { ... }
}
Angular will actually throw an error here,
Error: [$compile:ctreq] Controller '$http', required by directive 'componentA', can't be found!
It looks like it's registering all metadata against the parent class and not the child classes. Using ng-metadata to wire everything up behind the scenes.
@joesonw @rbuckton You can close this issue, it's not part of reflect metadata this was issue in angular it self. I misused api as well and it was bug in my code not in reflect metadata.
@igorzg does angular change something with Reflect? Because I get this issue when not running it through angular (though angular is present...):
import 'reflect-metadata';
const formatMetadataKey = Symbol('format');
const formKey = 'formData';
function Field (target: any, key: string) {
const formData = getFields(target) || [];
formData.push(key);
Reflect.metadata(formatMetadataKey, formData)(target, formKey);
}
export function getFields(target: any) {
return Reflect.getMetadata(formatMetadataKey, target, formKey);
}
class A {
@Field
foo: string;
@Field
bar: string;
}
class B extends A {
@Field
biz: string;
}
class C {
@Field
baz: string;
}
console.log(getFields(new A())); // ['foo', 'bar', 'biz']
console.log(getFields(new B())); // ['foo', 'bar', 'biz']
console.log(getFields(new C())); // ['baz']
EDIT: solved by being more defensive (ie: this comment, suggested above
I'm facing the same issue.
I have classes that extend from the same parent class and the metadata is stored on the parent and not the individual classes (or so it seems).
Unfortunately I have to admit that I don't understand the comments above. Is there a way to be "defensive" so that the metadata is only stored on the child class (whilst remaining that of the parent)?
Update: A theory I had was that the amount of alcohol consumed would affect my ability to both process the information and apply it properly. Further research has proven this theory right. I shall now slowly move backwards into the shadows and bow my head in shame.
Is there any update on this, I'm facing the exact same problem.
Same here.
import 'reflect-metadata';
const Searchable = (target: Object, property: string) => {
Reflect.defineMetadata('searchable', true, target, property);
console.log(target);// <--- this prints ParentClass, not ChildClass
}
abstract class ParentClass {
public id: string;
}
class ChildClass extends ParentClass {
@Searchable
public name: string;
}
Hi All, you can use https://typeix.com/packages/metadata/ which solves problem for you.
@igorzg I'm not sure that adding another npm package is really the solution here.
More broadly, however - the solution ended up being:
- console.log(target)
+ console.log(target.constructor)
Facing this same issue in version 0.2.1. If the decorator exists in only the child classes it works great. But as soon as the base class has a the same decorator it references the base class instead of the passed in target
class BaseEntity {
@Nullable() // <---- Remove this decorator to fix
SomeProp: string = '';
}
class FooEntity extends BaseEntity {
@Nullable()
AnotherProp: string = '';
}
class BarEntity extends BaseEntity {
@Nullable()
ThirdProp: string = '';
}
Reflect.getMetadata(Symbol('nullableKey'), FooEntity )
returns [SomeProp, AnotherProp, ThirdProp]
instead of [AnotherProp]
if the @Nullable()
decorator exists on the base class