Simon Hirsig
Simon Hirsig

Reputation: 93

readonly properties and ngOnInit

Readonly properties can only be assigned in the constructor, but in angular it is discouraged and sometimes impossible to use the constructor for certain initialization and instead the angular hook ngOnInit is used. Is there any way to mark ngOnInit as a constructor with regard to the readonly attribute so that I can use readonly for essentially immutable properties that are only assigned a single time in ngOnInit?

edit: To clarify: im not looking for alternative ways to declare readonly properties. I want to declare them the regular way. Anything else I think would not be worth the tradeoff in readability just to get that static check. I was hoping there would be some annotation like there are for tslint to ignore the immutability inside ngOnInit.

From the answers so far I guess assigning them as (this as any).foo = bar; is the closest to what I would like to do, though I guess it's still uglier than just leaving out the readonly.

Upvotes: 9

Views: 5186

Answers (4)

Pärt Johanson
Pärt Johanson

Reputation: 1650

That is a good question. You could get something similar going on via a setter:

...
    protected _pseudoReadonlyProp: number = -1 // or some other value, that marks unititialized state
    set pseudoReadonlyProp(a: number) {
        if(this._pseudoReadonlyProp != -1) {
           // throw some error here
        }
        this._pseudoReadonlyProp = a
    }
    get pseudoReadonlyProp(): number {
        return this._pseudoReadonlyProp
    }
...

Yet another way to get similar behavior would be to encapsulate the read only member in a class, put it in your component and create a new instance of the class in ngInit or some other component initialization function.

Considering Object.defineProperty exists, a better way would be to use it as pointed out by @trichetriche

Upvotes: 0

user4676340
user4676340

Reputation:

Readonly properties can only be assigned in the constructor

Not true

class MyClass {
  readonly prop1 = 'prop1';
  constructor(
    public readonly prop2 = 'prop2'
  ) {
  }

  ngOnInit() {
    Object.defineProperty(this, 'prop3', { wirtable: false, value: 'prop3'});
  }
}

As you see, there is already 3 ways of defining them. And probably more !

but in angular it is discouraged and sometimes impossible to use the constructor for certain initialization and instead the angular hook ngOnInit is used

It is "discouraged" for newcomers, because it's easier to tell them not to do that, rather than explaining in depth the lifecycle of the framework. The point is, you can use the constructor as you please, especially for stuff unrelated to Angular (like read-only variables).

Is there any way to mark ngOnInit as a constructor

A constrcutor is a constructor. You can't define a method to be a constructor. You can only call a method in the constructor, but that won't solve your issue.

so that I can use readonly for essentially immutable properties that are only assigned a single time in ngOnInit?

Answer and bottom line : use your constructor as you please. Put your readonly properties in it if you want.

Also, consider providing some sandbox or at least some code, so that we can make an answer that is adapted to both your code and your need.

Upvotes: 5

Christian Vincenzo Traina
Christian Vincenzo Traina

Reputation: 10424

Why don't you use a setter and a getter in place of readonly?

export class MyComponent {
  private _value: string;
  set value(v: string) {
    if (!this._value) {
      this._value = v;
    } else {
      throw new TypeError('Assignment to read-only variable');
    }
  }

  get value() {
    return this._value;
  }
}

Upvotes: 2

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249746

You can't mark a method as a constructor there is just no syntax for that. You can break the readonly which is just a compile time check by using a type assertion to any and access any public/private property you want in a type unsafe way. You can also use a mapped type to make the type mutable, but it only works for public properties:

type Mutable<T> = { -readonly [ P in keyof T]: T[P] }

class Foo {
    public readonly data: string;
    private readonly pdata: string;
    public init() {
        const ref: Mutable<this> = this;
        ref.data = "" 
        const pRef = this as any;
        pRef.pdata = ""

        const pSaferRef: { pdata: string } = this as any;
        pSaferRef.pdata = ""
    }
}

Upvotes: 3

Related Questions