If I have a super class that is supposed to be able to set some initial properties on a new instance, why do the members of the child class seem to override the constructor of the super class?
class Base {
constructor(fields) {
Object.assign(this, fields)
}
}
class Foo extends Base {
time = 0;
// calling super seems to make no difference
// constructor(...args) {
// super(...args)
// }
}
const foo = new Foo({ time: 22 })
console.log(foo.time) // is 0, should be 22?
If I remove time = 0; from the child class it works as expected but my linter breaks.
How can I retain the member initializer, i.e., time = 0; in the child class but have the super class be able to set properties in its constructor?
Instances may only be accessed after a super call is complete. Without the class field, look at the error:
class Base {
constructor(fields) {
Object.assign(this, fields)
}
}
class Foo extends Base {
constructor(...args) {
this.time = 0;
super(...args)
}
}
const foo = new Foo({ time: 22 })
Must call super constructor in derived class before accessing 'this' or returning from derived constructor
Class fields follow this same logic by assigning to the instance only after the super call is complete. So
class Foo extends Base {
time = 0;
constructor(...args) {
super(...args)
is equivalent to
class Foo extends Base {
constructor(...args) {
super(...args)
this.time = 0;
In other words - the properties assigned inside the constructor (or class field) of a subclass is designed to override the properties assigned in a superclass constructor.
but have the super class be able to set properties in its constructor?
One approach would be to the default value for the time property to 0, then pass and use that instead of a class field:
class Base {
constructor(fields) {
Object.assign(this, fields)
}
}
class Foo extends Base {
constructor({ time = 0, ...rest }) {
super({ time, ...rest })
}
}
const foo = new Foo({ time: 22 })
console.log(foo.time)
Related
I am having this problem with subclasses overriding class property in JS. I think the behavior in OOP is what I want, but I wish to achieve the same in JS.
class Base {
constructor () {
console.log(this.a)
}
}
class Child extends Base {
a = 1
}
new Child()
// undefined, but I expect (want) 1
How do I modify my code to log 1?
Example in Python, that works:
class Base:
def __init__(self):
print(self.a)
class Child(Base):
a = 1
Child()
# 1 -> desirable
undefined output is expected behavior. You haven't defined the property a in the base class so it is just natural that base class constructor outputs undefined in the console.
class Base {
constructor () {
console.log('Base constructor', this.a) // outputs undefined
}
}
class Child extends Base {
a = 1
constructor () {
super();
console.log('Child constructor', this.a); // outputs 1
}
}
new Child()
UPDATE
In Python, __init__ function is not a real constructor. Constructor is __new__.
This is what I ended up doing:
class Base {
constructor () {
console.log(this.a)
}
}
class Child extends Base {
get a () { return 1 }
}
new Child()
I am trying understand mixins in javascript and all the examples and articles i have read so far talk about adding methods and not properties.
I have found Alex Jover Morales' article really useful and i have slightly modified his example to include an additional mixin and constructors with new properties within the mixins here.
Is what i have done below an anti-pattern?
Is there a problem with having a constructor and properties within a mixin?
Is there a problem with calling super() within each mixin's contructor?
const PlayMixin = superclass => class extends superclass {
constructor(args) {
let { favouriteGame } = args
super(args);
this.favouriteGame=favouriteGame;
}
play() {
console.log(`${this.name} is playing ${this.favouriteGame}`);
}
};
const FoodMixin = superclass => class extends superclass {
constructor(args) {
let { genericFood } = args
super(args);
this.genericFood=genericFood;
}
eat() {
console.log(`${this.name} is eating ${this.genericFood}`);
}
poop() {
console.log("Going to 💩");
}
};
class Animal {
constructor(args) {
let {name} = args
this.name = name
}
}
class Dog extends PlayMixin(FoodMixin(Animal)) {
constructor(...args) {
super(...args)
}
bark() {
console.log("Woff woff!")
}
haveLunch() {
this.eat();
this.poop();
}
}
const jack = new Dog({name:"Jack", genericFood:"lobster",
favouriteGame:"chess"});
jack.haveLunch();
jack.play();
.as-console-wrapper { max-height: 100%!important; top: 0; }
Is what i have done below an anti-pattern?
No, it is not.
Is there a problem with having a constructor and properties within a mixin?
No, as long as you call super(...) in a manner that it works for all mixed in classes.
Is there a problem with calling super() within each mixin's contructor?
No, super always points to the extended class, there isno problem in calling that constructor.
I have a class A, and a class B inherited from it.
class A {
constructor(){
this.init();
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
When I run this code, I get the following error:
Uncaught TypeError: Cannot read property 'value' of undefined
How can I avoid this error?
It's clear for me that the JavaScript code will call the init method before it creates the myMember, but there should be some practice/pattern to make it work.
This is why in some languages (cough C#) code analysis tools flag usage of virtual members inside constructors.
In Typescript field initializations happen in the constructor, after the call to the base constructor. The fact that field initializations are written near the field is just syntactic sugar. If we look at the generated code the problem becomes clear:
function B() {
var _this = _super.call(this) || this; // base call here, field has not been set, init will be called
_this.myMember = { value: 1 }; // field init here
return _this;
}
You should consider a solution where init is either called from outside the instance, and not in the constructor:
class A {
constructor(){
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
x.init();
Or you can have an extra parameter to your constructor that specifies whether to call init and not call it in the derived class as well.
class A {
constructor()
constructor(doInit: boolean)
constructor(doInit?: boolean){
if(doInit || true)this.init();
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor()
constructor(doInit: boolean)
constructor(doInit?: boolean){
super(false);
if(doInit || true)this.init();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
Or the very very very dirty solution of setTimeout, which will defer initialization until the current frame completes. This will let the parent constructor call to complete, but there will be an interim between constructor call and when the timeout expires when the object has not been inited
class A {
constructor(){
setTimeout(()=> this.init(), 1);
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
// x is not yet inited ! but will be soon
Because myMember property is accessed in parent constructor (init() is called during super() call), there is no way how it can be defined in child constructor without hitting a race condition.
There are several alternative approaches.
init hook
init is considered a hook that shouldn't be called in class constructor. Instead, it is called explicitly:
new B();
B.init();
Or it is called implicitly by the framework, as a part of application lifecycle.
Static property
If a property is supposed to be a constant, it can be static property.
This is the most efficient way because this is what static members are for, but the syntax may be not that attractive because it requires to use this.constructor instead of class name if static property should be properly referred in child classes:
class B extends A {
static readonly myMember = { value: 1 };
init() {
console.log((this.constructor as typeof B).myMember.value);
}
}
Property getter/setter
Property descriptor can be defined on class prototype with get/set syntax. If a property is supposed to be primitive constant, it can be just a getter:
class B extends A {
get myMember() {
return 1;
}
init() {
console.log(this.myMember);
}
}
It becomes more hacky if the property is not constant or primitive:
class B extends A {
private _myMember?: { value: number };
get myMember() {
if (!('_myMember' in this)) {
this._myMember = { value: 1 };
}
return this._myMember!;
}
set myMember(v) {
this._myMember = v;
}
init() {
console.log(this.myMember.value);
}
}
In-place initialization
A property may be initialized where it's accessed first. If this happens in init method where this can be accessed prior to B class constructor, this should happen there:
class B extends A {
private myMember?: { value: number };
init() {
this.myMember = { value: 1 };
console.log(this.myMember.value);
}
}
Asynchronous initialization
init method may become asynchronous. Initialization state should be trackable, so the class should implement some API for that, e.g. promise-based:
class A {
initialization = Promise.resolve();
constructor(){
this.init();
}
init(){}
}
class B extends A {
private myMember = {value:1};
init(){
this.initialization = this.initialization.then(() => {
console.log(this.myMember.value);
});
}
}
const x = new B();
x.initialization.then(() => {
// class is initialized
})
This approach may be considered antipattern for this particular case because initialization routine is intrinsically synchronous, but it may be suitable for asynchronous initialization routines.
Desugared class
Since ES6 classes have limitations on the use of this prior to super, child class can be desugared to a function to evade this limitation:
interface B extends A {}
interface BPrivate extends B {
myMember: { value: number };
}
interface BStatic extends A {
new(): B;
}
const B = <BStatic><Function>function B(this: BPrivate) {
this.myMember = { value: 1 };
return A.call(this);
}
B.prototype.init = function () {
console.log(this.myMember.value);
}
This is rarely a good option, because desugared class should be additionally typed in TypeScript. This also won't work with native parent classes (TypeScript es6 and esnext target).
One approach you could take is use a getter/setter for myMember and manage the default value in the getter. This would prevent the undefined problem and allow you to keep almost exactly the same structure you have. Like this:
class A {
constructor(){
this.init();
}
init(){}
}
class B extends A {
private _myMember;
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
get myMember() {
return this._myMember || { value: 1 };
}
set myMember(val) {
this._myMember = val;
}
}
const x = new B();
Try this:
class A {
constructor() {
this.init();
}
init() { }
}
class B extends A {
private myMember = { 'value': 1 };
constructor() {
super();
}
init() {
this.myMember = { 'value': 1 };
console.log(this.myMember.value);
}
}
const x = new B();
Super has to be first command. Remeber that typescript is more "javascript with documentation of types" rather than language on its own.
If you look to the transpiled code .js it is clearly visible:
class A {
constructor() {
this.init();
}
init() {
}
}
class B extends A {
constructor() {
super();
this.myMember = { value: 1 };
}
init() {
console.log(this.myMember.value);
}
}
const x = new B();
Do you have to call init in class A?
That works fine, but I don't know if you have different requirements:
class A {
constructor(){}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
this.init();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
More often than not, you can defer the call of init() to a time just before it is needed by hacking into one of your getters.
For example:
class FoodieParent {
public init() {
favoriteFood = "Salad";
}
public _favoriteFood: string;
public set favoriteFood(val) { this._favoriteFood = val; }
public get favoriteFood() {
if (!this._favoriteFood) {
this.init();
}
return this._favoriteFood;
}
public talkAboutFood() {
// init function automatically gets called just in time, because "favoriteFood" is a getter
console.log(`I love ${this.favoriteFood}`);
}
}
// overloading the init function works without having to call `init()` afterwards
class FoodieChild extends FoodieParent {
public init() {
this.favoriteFood = "Pizza"
}
}
Like this :
class A
{
myMember;
constructor() {
}
show() {
alert(this.myMember.value);
}
}
class B extends A {
public myMember = {value:1};
constructor() {
super();
}
}
const test = new B;
test.show();
I have a class A, and a class B inherited from it.
class A {
constructor(){
this.init();
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
When I run this code, I get the following error:
Uncaught TypeError: Cannot read property 'value' of undefined
How can I avoid this error?
It's clear for me that the JavaScript code will call the init method before it creates the myMember, but there should be some practice/pattern to make it work.
This is why in some languages (cough C#) code analysis tools flag usage of virtual members inside constructors.
In Typescript field initializations happen in the constructor, after the call to the base constructor. The fact that field initializations are written near the field is just syntactic sugar. If we look at the generated code the problem becomes clear:
function B() {
var _this = _super.call(this) || this; // base call here, field has not been set, init will be called
_this.myMember = { value: 1 }; // field init here
return _this;
}
You should consider a solution where init is either called from outside the instance, and not in the constructor:
class A {
constructor(){
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
x.init();
Or you can have an extra parameter to your constructor that specifies whether to call init and not call it in the derived class as well.
class A {
constructor()
constructor(doInit: boolean)
constructor(doInit?: boolean){
if(doInit || true)this.init();
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor()
constructor(doInit: boolean)
constructor(doInit?: boolean){
super(false);
if(doInit || true)this.init();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
Or the very very very dirty solution of setTimeout, which will defer initialization until the current frame completes. This will let the parent constructor call to complete, but there will be an interim between constructor call and when the timeout expires when the object has not been inited
class A {
constructor(){
setTimeout(()=> this.init(), 1);
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
// x is not yet inited ! but will be soon
Because myMember property is accessed in parent constructor (init() is called during super() call), there is no way how it can be defined in child constructor without hitting a race condition.
There are several alternative approaches.
init hook
init is considered a hook that shouldn't be called in class constructor. Instead, it is called explicitly:
new B();
B.init();
Or it is called implicitly by the framework, as a part of application lifecycle.
Static property
If a property is supposed to be a constant, it can be static property.
This is the most efficient way because this is what static members are for, but the syntax may be not that attractive because it requires to use this.constructor instead of class name if static property should be properly referred in child classes:
class B extends A {
static readonly myMember = { value: 1 };
init() {
console.log((this.constructor as typeof B).myMember.value);
}
}
Property getter/setter
Property descriptor can be defined on class prototype with get/set syntax. If a property is supposed to be primitive constant, it can be just a getter:
class B extends A {
get myMember() {
return 1;
}
init() {
console.log(this.myMember);
}
}
It becomes more hacky if the property is not constant or primitive:
class B extends A {
private _myMember?: { value: number };
get myMember() {
if (!('_myMember' in this)) {
this._myMember = { value: 1 };
}
return this._myMember!;
}
set myMember(v) {
this._myMember = v;
}
init() {
console.log(this.myMember.value);
}
}
In-place initialization
A property may be initialized where it's accessed first. If this happens in init method where this can be accessed prior to B class constructor, this should happen there:
class B extends A {
private myMember?: { value: number };
init() {
this.myMember = { value: 1 };
console.log(this.myMember.value);
}
}
Asynchronous initialization
init method may become asynchronous. Initialization state should be trackable, so the class should implement some API for that, e.g. promise-based:
class A {
initialization = Promise.resolve();
constructor(){
this.init();
}
init(){}
}
class B extends A {
private myMember = {value:1};
init(){
this.initialization = this.initialization.then(() => {
console.log(this.myMember.value);
});
}
}
const x = new B();
x.initialization.then(() => {
// class is initialized
})
This approach may be considered antipattern for this particular case because initialization routine is intrinsically synchronous, but it may be suitable for asynchronous initialization routines.
Desugared class
Since ES6 classes have limitations on the use of this prior to super, child class can be desugared to a function to evade this limitation:
interface B extends A {}
interface BPrivate extends B {
myMember: { value: number };
}
interface BStatic extends A {
new(): B;
}
const B = <BStatic><Function>function B(this: BPrivate) {
this.myMember = { value: 1 };
return A.call(this);
}
B.prototype.init = function () {
console.log(this.myMember.value);
}
This is rarely a good option, because desugared class should be additionally typed in TypeScript. This also won't work with native parent classes (TypeScript es6 and esnext target).
One approach you could take is use a getter/setter for myMember and manage the default value in the getter. This would prevent the undefined problem and allow you to keep almost exactly the same structure you have. Like this:
class A {
constructor(){
this.init();
}
init(){}
}
class B extends A {
private _myMember;
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
get myMember() {
return this._myMember || { value: 1 };
}
set myMember(val) {
this._myMember = val;
}
}
const x = new B();
Try this:
class A {
constructor() {
this.init();
}
init() { }
}
class B extends A {
private myMember = { 'value': 1 };
constructor() {
super();
}
init() {
this.myMember = { 'value': 1 };
console.log(this.myMember.value);
}
}
const x = new B();
Super has to be first command. Remeber that typescript is more "javascript with documentation of types" rather than language on its own.
If you look to the transpiled code .js it is clearly visible:
class A {
constructor() {
this.init();
}
init() {
}
}
class B extends A {
constructor() {
super();
this.myMember = { value: 1 };
}
init() {
console.log(this.myMember.value);
}
}
const x = new B();
Do you have to call init in class A?
That works fine, but I don't know if you have different requirements:
class A {
constructor(){}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
this.init();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
More often than not, you can defer the call of init() to a time just before it is needed by hacking into one of your getters.
For example:
class FoodieParent {
public init() {
favoriteFood = "Salad";
}
public _favoriteFood: string;
public set favoriteFood(val) { this._favoriteFood = val; }
public get favoriteFood() {
if (!this._favoriteFood) {
this.init();
}
return this._favoriteFood;
}
public talkAboutFood() {
// init function automatically gets called just in time, because "favoriteFood" is a getter
console.log(`I love ${this.favoriteFood}`);
}
}
// overloading the init function works without having to call `init()` afterwards
class FoodieChild extends FoodieParent {
public init() {
this.favoriteFood = "Pizza"
}
}
Like this :
class A
{
myMember;
constructor() {
}
show() {
alert(this.myMember.value);
}
}
class B extends A {
public myMember = {value:1};
constructor() {
super();
}
}
const test = new B;
test.show();
I have TypeScript (JavaScript) class like this:
import * as React from 'react'
export default
class StyleableComponent<PROPS, STATE> extends React.Component<PROPS, STATE> {
protected classes: any
static style: any
someMethod() {
const ctor = this.constructor
console.log(this.constructor.style)
}
}
and TypeScript throws this error:
ERROR in ./src/StyleableComponent.ts
(11,38): error TS2339: Property 'style' does not exist on type 'Function'.
But, obviously, you can see that static style: any is declared in the class definition.
So, how do we work with this.constructor properly? Note, this.constructor can be the constructor of a class that extends StyleableComponent, so this.constructor may not be === StyleableComponent, but it should have the style property because it extends from StyleableComponent.
For example,
interface P {...}
interface S {...}
class Foo extends StyleableComponent<P,S> {...}
console.log(new Foo)
^ this.constructor will be Foo, and the StyleableComponent class needs to look at Foo.style.
So how do I do this? Do I need to use a extra template type parameter somehow?
Static Property V.S Instance Property Inheritance
If you want read static property from subclass,you can do it with 3 ways.for more details you can see Test section.
because Object.getPrototypeOf(..)'s return type is any, so you can access style directly,for example:
someMethod() {
let style = Object.getPrototypeOf(this).constructor.style;
}
because this.constructor's return type is a Function, so you must assign it to a any variable at first,for example:
someMethod() {
let cotr:any=this.constructor;
let style = cotr.style;
}
because Function is an interface you can expand it in typescript,for example:
declare global{
interface Function{
style:any;
}
}
someMethod() {
return this.constructor.style;
}
and you can also do it with replace static property with instance property instead.if you want read subclass style property you must define the property on constructor,then the subclass can choose define its style by pass the style to the superclass or not at all.for example:
constructor(protected style:any="default"){
}
the interesting is that the subclass behavior are all the same except the style property.In design view, if you use the static style properties you must define another subclass to achieve it,this will tends to many subclass with diff styles.but when use instance property style,you can do it by pass the style with optional for different style only.for example:
let bar=new Bar();//use parent's style
let baz=new Bar(null,null,"baz");//use it owned style
and you can also reject others to pass their style by pass the style in constructor of the subclass.for example:
constructor(){
super("style");
}
Tests
import * as React from 'react';
declare global {
interface Function {
style: any
}
}
describe('static inheritance', () => {
class StyleableComponent<P, S> extends React.Component<P, S> {
protected classes: any;
static style: any;
constructor(props?: P, context?: any, public style: any = "default") {
super(props, context);
}
someMethod() {
//dangerous if subclass not define static style property
//todo:the 1st approach
// let style = Object.getPrototypeOf(this).constructor.style;
// return style;
//todo:the 2nd approach
// let cotr: any = this.constructor;
// return cotr.style;
//todo:the 3nd approach,you must declare style in Function interface
return this.constructor.style;
}
}
class Foo extends StyleableComponent<any, any> {
static style = "foo";
constructor(props?: any, context?: any) {
super(props, context, Foo.style);
}
}
class Bar extends StyleableComponent<any, any> {
}
test('access style from subclass', function () {
let foo = new Foo();
expect(foo.someMethod()).toBe(Foo.style);
});
test('return undefined if subclass not define style', function () {
let bar = new Bar();
expect(bar.someMethod()).toBeUndefined();
});
test('replace static style with instance property', function () {
let foo = new Foo();
let bar = new Bar();
let baz = new Bar(null, null, "baz");
expect(foo.style).toBe("foo");
expect(bar.style).toBe("default");
expect(baz.style).toBe("baz");
});
});