Javascript base class constructor run with subclass (overridden) property - javascript

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

Related

Child class member initializer overriding parent class constructor

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)

Accessing base class properties in derived class using prototype/Access base class properties in the decorator

I am using typescript and i am writing a custom decorator for one of my angular class. I want to access the base class method in the child class decorator. Or access base class methods using the child class prototype. Is there any way to do this? Problem explained in detail below.
Scenario
I have a base class which is like
export class Base {
public init() {
console.log('My base class function');
}
}
And i have a derived class which extends this base class
export class Child extends Base {
}
What i am trying to do
I am trying to write a decorator for the derived class something like
#TestDecorator(['init'])
export class Child extends Base {
}
which will call the init method from the base class.
What is the issue
To do the above scenario, i have written code something like below
export function Tool<T extends Base>(methods: any[]) {
return function (target: Function) {
methods.forEach((item) => {
if (item === 'init') {
target.super.init() // Stuck here
}
})
}
}
I am not understanding how to make the following line work
target.super.init() // Stuck here
Please help me with the solution. I am stuck. Thanks
I believe you are looking for something like this:
export function Tool<T extends Base>(methods: any[]) {
return function (target: Function) {
return class extends target {
constructor(...args: any[]) {
super(...args)
methods.forEach((item) => {
if (item === 'init') {
super.init( );
}
})
}
}
}
}
To expand on Paulpro's answer, since the decorator function is returning a substitute for the constructor of the class that it is decorating, it must maintain the original prototype.
In the following example, there is an error due to the missing init() method in TestDecorator<Base>.
Typescript Playground Demo
class Base {
public init() {
console.log('My base class function');
}
}
function TestDecorator<T extends Base>(methods: any[]) {
return function (target: any) {
return class extends target {
constructor(...args: any[]) {
super(...args)
methods.forEach((item) => {
if (item === 'init') {
super.init( );
}
})
}
}
}
}
#TestDecorator(['init']) // Error: Property 'init' is missing in type 'TestDecorator<Base>.(Anonymous class)' but required in type 'Child'.
class Child extends Base {
}
let c = new Child();
Corrected Decorator
function TestDecorator<T extends Base>(methods: any[]) {
return function (target: any) {
return class extends target {
init() {} // Define init()
constructor(...args: any[]) {
super(...args)
methods.forEach((item) => {
if (item === 'init') {
super.init( );
}
})
}
}
}
}
Class Decorators
If the class decorator returns a value, it will replace the class declaration with the provided constructor function.
NOTE: Should you choose to return a new constructor function, you must take care to maintain the original prototype. The logic that applies decorators at runtime will not do this for you.

How to have a Class.SubClass.SomeMethod structure?

This is my main class (it uses a subclass:)
import SubClass from './SubClass'
class MainClass extends classes(SubClass) {
constructor () {
// some code
}
}
window.MainClass = new MainClass()
export default MainClass
This is the subclass:
class SubClass {
constructor () {
this.someMethod = function () {
// some code
}
}
}
export default SubClass
If I want to use a method from the SubClass I can write: MainClass.someMethod.
How to modify this code so I write: MainClass.SubClass.someMethod instead?
So I can write:
MainClass.SubClass.someMethod
MainClass.SubClass2.someMethod
In case I need another SubClass?
I think you need to call super(). And classes() seem doen't need to be added.
When used in a constructor, the super keyword appears alone and must be used before the this keyword is used.
See document
import SubClass from './SubClass'
class MainClass extends SubClass {
constructor () {
super();
console.log(this.someMethod)
}
}
Hope this help

How to reference static properties on `this.constructor` inside base class code?

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

How to override a parent class method in React?

I'm extending a base class and overriding a method in the base class. But when I call it, it calls the super class version. How do I override the method?
var Hello = React.createClass( {
getName: function() { return "super" },
render: function() {
return <div>This is: {this.getName()}</div>;
}
});
class HelloChild extends Hello {
constructor(props) {
super(props);
console.log( this.getName());
}
getName()
{
return "Child";
}
};
I want it to print "This is: Child" but it prints "This is: super"
The problem is that you're mixing ES6 type class declaration (ex. Hello) with old school Javascript declaration (ex. HelloChild). To fix HelloChild, bind the method to the class.
class HelloChild extends Hello {
constructor(props) {
super(props);
this.getName = this.getName.bind(this); // This is important
console.log( this.getName());
}
getName()
{
return "Child";
}
};
Then it'll work.
I found the answer (adapted from here: https://gist.github.com/Zodiase/af44115098b20d69c531 ) - the base class needs to also be defined in an ES6 manner:
class Hello extends React.Component {
//abstract getName()
getName()
{
if (new.target === Hello) {
throw new TypeError("method not implemented");
}
}
render() {
return <div>This is: {this.getName()}</div>;
}
};
Actually you can override method to execute code from your subclass
class Hello extends React.Component {
getName() {
super.getName();
}
}
class HelloChild extends Hello {
getName()
{
return "Child";
}
}
Please note that this answer proposes different approach:
I wonder why you should do this in the first place, my point is that directly coupling two react components is not a right way to implement re-usability in React.
If you are trying to have multiple child components which extends one parent, What I would do is, to have child components and a higher-order component and then implement common functionality with Composition. This way you can skip those methods, which you were trying to override and so everything would stay clear.

Categories