Use a property decorator to initiate a class

I'm trying to create a property decorator in Angular 7 that initiates a class on the set variable

export class NewDataClass() {
   public readonly status = { loaded: false }
   public greet() {
      return 'hello';
   }
}

And build a decorator that returns that new class, with some parameters

export function NStatus() {
  return function(target: Object, key: string | symbol): any {

    return new NewDataClass();
  };
}

@Component({
  selector: 'app-new',
  templateUrl: './new.component.n.html',
  styleUrls: ['./new.component.n.scss']
})
export class NewComponent implements OnInit, OnDestroy {
  @NStatus() public status: NewDataClass;
}

When the component is initialized the status should have the value of new NewDataClass. Help

1 answer

  • answered 2019-05-22 06:38 Titian Cernicova-Dragomir

    You can't do this directly from a decorator. A decorator is invoked on class creation (not instantiation) so all we can do is change the class itself to suit our needs.

    One option would be to transform the field into a property and instantiate the value on the getter:

    function NStatus() {
        return function (target: Object, key: string, desc?: PropertyDescriptor): PropertyDescriptor {
            return {
                get: function (this: Record<string, NewDataClass>) {
                    return this["_" + key] || (this["_" + key] = new NewDataClass())
                },
                set: function (this: Record<string, NewDataClass>, value: NewDataClass) {
                    this["_" + key] = value
                }
            }
        }
    }
    
    class NewComponent {
        @NStatus() public status: NewDataClass;
    }
    
    console.log(new NewComponent().status);
    

    Another option, since we are talking about angular components we could override ngOnInit from the decorator and perform the initialization there:

    function NStatus() {
        return function <K extends PropertyKey>(target: { ngOnInit?: () => void  }, key: K): void {
            const originalNgOnInit = target.ngOnInit;
            target.ngOnInit = function (this: Record<K, NewDataClass>) {
                // Init the field
                this[key] = new NewDataClass;
                // Call original ngOnInit if any
                if(originalNgOnInit) originalNgOnInit.call(this);
            }
        }
    }
    
    class NewComponent {
        @NStatus() public status: NewDataClass;
        ngOnInit () {}
    }
    
    const c = new NewComponent()
    c.ngOnInit();
    console.log(c.status);
    

    Intercepting the class constructor from a property descriptor is not possible unfortunately.