-1

One solution not discussed by this answer is to implement a get method in a derived class which returns a string literal (but not member property as this would essentially be the same issue):

class Base {
    constructor() { console.log( this.color ) }
    public get color(): string { return 'blue' }
}

class Literal extends Base {
    public get color(): string { return 'red' }
}

class Member extends Base {
    private _color: string = 'green'
    public get color(): string { return this._color }
}

let b = new Base() // => 'blue'
let l = new Literal() // => 'red'
let m = new Member() // => undefined

Are there any issues/downfalls to using this approach, for example with efficiency, in the emitted JavaScript - as opposed to the solutions provided in the linked answer?

Callan Heard
  • 727
  • 1
  • 8
  • 18
  • The obvious drawback: you cannot change (assign) the property? – Bergi Feb 14 '18 at 16:52
  • It is not clear to me, which performance scenario you are trying... accessing to properties of the base / derived class from the construction ? – juan garcia Feb 14 '18 at 17:35
  • 1
    Observing virtual behavior from the constructor is really never going to be completely safe. You have no way to enforce that derived classes don't need other parts of their initialization in the getter body – Ryan Cavanaugh Feb 14 '18 at 18:30
  • @RyanCavanaugh This issue can be addressed by moving a prop to class proto. Sadly, TS doesn't provide support for proto props, so it's a hack that cannot benefit from type checks (while proto methods can). – Estus Flask Feb 14 '18 at 19:54

1 Answers1

0

A usual implication of class fields is that they provide overhead in child classes:

class Member extends Base {
    protected _color: string = 'green'
    public get color(): string { return this._color }
}

class Foo extends Member {
    protected _color: string = 'orange'
}

_color will be assigned to 'green' and then to 'orange' on Foo instantiation. Notice that _color should be protected in order to be reassigned.

This is not a big concern for string value but it exists and may be a problem if _color is complex object that doesn't need to be instantiated for each instance.

This can be solved by moving a property to class prototype:

interface Member extends Base {
  _color: IComplexColor;
}
class Member extends Base {
    public get color(): string { return this._color.toString() }
}
Member.prototype._color = new SomeComplexColorClass('green');

class Foo extends Member {}
Foo.prototype._color = new SomeComplexColorClass('orange');

Notice that in this case class declaration should be merged with interface, this results in _color being public, because protected _color; without initializer would cause a problem with strictPropertyInitialization compiler option. This solution isn't idiomatic to TypeScript because it needs workarounds for typing system, but it results in cleaner output.

This can be considered preliminary optimization for a class with string property and relatively short prototype chain.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565