Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decorated class inheritance issue #62

Open
joesonw opened this issue Feb 28, 2017 · 10 comments
Open

Decorated class inheritance issue #62

joesonw opened this issue Feb 28, 2017 · 10 comments

Comments

@joesonw
Copy link

joesonw commented Feb 28, 2017

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

@rbuckton
Copy link
Owner

rbuckton commented Mar 2, 2017

What does SomeDecorator look like? If it's anything like the decorator mentioned in #53, check #53 (comment)

@mtraynham
Copy link

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.

@igorzg
Copy link

igorzg commented Dec 4, 2017

@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.

@schtauffen
Copy link

schtauffen commented Apr 14, 2018

@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

@RWOverdijk
Copy link

RWOverdijk commented Mar 30, 2019

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.

@Tonio-lavanda
Copy link

Is there any update on this, I'm facing the exact same problem.

@robertmain
Copy link

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;
}

@igorzg
Copy link

igorzg commented May 25, 2023

Hi All, you can use https://typeix.com/packages/metadata/ which solves problem for you.

@robertmain
Copy link

@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)

@polklabs
Copy link

polklabs commented Feb 3, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants