ES6 importing extended classes - javascript

So I have come across an interesting issue while trying to extend a class to use in another class and then import it to another file.
'class-test.js':
export default class MyClass {
constructor () {
this.date_created = new Date()
this.posts = new Posts()
}
}
class Posts extends Array {
add (val) {
this.push(val)
}
}
Then when I create a new MyClass instance in another file (and import MyClass from class-test.js), the myClass.posts property is only being seen as an Array and so doesn't have the extended function add()
I think the problem is that the Posts class is not being moved with the MyClass class; but without casting I have no idea how to tell it to use that class.
Where I'm particularly frustrated is it works fine if all in one file:
class MyClass {
constructor () {
this.date_created = new Date()
this.posts = new Posts()
}
}
class Posts extends Array {
add (val) {
this.push(val)
}
}
var x = new MyClass('as', 'asd')
x.posts.add('asdf')
console.log(x.posts)
x.posts.add('qwer')
x.posts.add('zxcv')
console.log(x.posts)

Did you try to export, and import both classes ?
'class-def.js':
class MyClass {
constructor () {
this.date_created = new Date()
this.posts = new Posts()
}
}
class Posts extends Array {
add (val) {
this.push(val)
}
}
export { MyClass, Posts };
'class-test.js':
import { MyClass, Posts } from 'class-def.js';
var x = new MyClass('as', 'asd')
x.posts.add('asdf')
console.log(x.posts)
x.posts.add('qwer')
x.posts.add('zxcv')
console.log(x.posts)

Related

Method substitution between objects

In nodejs, typescript, I want to substitute a method of an object with a method of another object; I have written the following very simple example to better understand my problem (the real situation is, more or less, the same):
export default class A_01 {
constructor(private variableA1: string) {}
public writeSomething() {
console.log(`${this.variableA1} from class A`);
}
}
import A_01 from "./oop_class_A";
export default class B_01 extends A_01 {
constructor(private variableB1: string) {
super(variableB1);
}
public writeSomething() {
console.log(`${this.variableB1} from class B`);
}
}
import A_01 from "./oop_class_A";
class C_01 {
constructor() {}
run() {
return new A_01("Object A_01 from class C_01"); // cannot modify this object creation!!!
}
}
import A_01 from "./oop_class_A";
import B_01 from "./oop_class_B";
const D_01 = new A_01("from_class_D_01");
D_01.writeSomething();
So, how to print from_class_D_01 from class B (and NOT from class A) ?
I have tried casting
const D_01 = new A_01("from_class_D_01") as B_01
but it's only a type and I lose it at runtime.
Not sure if this is what you need, this is a very hacky way to overwrite the writeSomething method after an A_01 instance has been created.
const D_01 = new A_01("from_class_D_01")
D_01.writeSomething = B_01.prototype.writeSomething
D_01.writeSomething()
Now it will write "from class B" even though it's an instance of A_01

JS: TypeError: Class extends value <ClassName> is not a constructor or null

I write UI tests using PageObject Pattern (NodeJS + Webdriverio) and I have a base class (BasePage), a page class(MyPage), a popup component class(PopupComponentClass) with multipurpose behaviour, a popup class with a specific implementation(SpecificPopupComponentClass). I need to extend SpecificPopupComponentClass from PopupComponentClass.
page.js:
export default class BasePage {
get BASE_URL() {
return "https://url.com";
};
...some methods...
}
my.page.js:
import BasePage from "../page";
class MyPage extends BasePage {
constructor() {
super();
};
get URL() { return `${this.BASE_URL}/some/path` };
get title() { return $("h1") };
orderRandomTariff() {
...some actions...
};
}
export default new MyPage ();
popup.component.page.js:
import BasePage from "../pages/page";
class PopupComponent extends BasePage{
constructor() {
super();
};
get title() { return $("h1") };
}
export default new PopupComponent();
specific.popup.component.js:
import PopupComponent from "./popupFragment";
class SpecificPopupComponent extends PopupComponent {
constructor() {
super();
};
get popupStreetInput() { return $(".//div[#class='checkAddress']//*[./*[contains(text(),'Street')]]//input") };
fillStreet(street) {
...some actions with this.popupStreetInput...
};
}
export default new SpecificPopupComponent();
...and trying to use it in test.js:
import MyPage from "../../../../pages/my.page";
import SpecificPopupComponent from "../../../../fragments/specific.popup.component";
const myPage= MyPage ;
const popup = SpecificPopupComponent ;
describe("Test", () => {
before(() => {
myPage.open();
});
it("Check", () => {
popup.fillStreet("Street");
});
});
but I'm getting an error: "TypeError: Class extends vlaue #PopupComponent is not a constructor or null".
I suspect this is due to circular dependencies, but I can't understand, what I need to do to fix that.
It seems the issue here is how you're exporting the base class. You're instantiating it instead of just exporting the class you'll inherit from. It should be like this:
export default class PopupComponent extends BasePage {
constructor() {
super();
};
get title() { return $("h1") };
}
And then create the instance when you're going to use it
var myPage = new MyPage()
var popup = new SpecificPopupComponent()
myPage.open()
popup.fillStreet('street')
What you're doing there is equivalent to doing this:
class SpecificPopupComponent extends new PopupComponent() {
// ... what am I? 🤔
}
I suspect this is due to circular dependencies
You can validate or reject your suspects by copying all the code into the test file in the correct order without using imports. But I don't think it's an circular dependency
I created this Plunker to remove the usage of imports and prove this.

Class Extensions in Modular JS

Say I have a generic class module:
export class MyCalc {
data = {}
...
}
And say I want to extend more functionality:
export class MyCalcLoader {
load = some_data => {
this.data = some_data;
}
}
export class MyCalcUI {
print = () => {
document.write(JSON.stringify(this.data));
}
}
What is the appropriate way to extend MyCalc and also use those extensions/plugins?
import {MyCalc} from "./MyCalc.js";
import {MyCalcLoader} from "./MyCalcLoader.js";
import {MyCalcUI} from "./MyCalcUI.js";
// TODO: MakeMyCalcExtendLoaderAndUi();
class BankingCalc extends MyCalc {
config = {...}
constructor() {
super();
}
}
const banking_calc = new BankingCalc();
banking_calc.load({...});
banking_calc.print();
I've thought through a few different janky ways to do this, but I'm sure this is common enough and that there's a right way to do it with vanilla ES6.
You could use Mixins:
export const MyCalcLoader = Super => class MyCalcLoader extends Super {
load = some_data => {
this.data = some_data;
}
}
export const MyCalcUI = Super => class MyCalcUI extends Super {
print = () => {
document.write(JSON.stringify(this.data));
}
}
Then compose the class as:
class BankingCalc extends MyCalcLoader(MyCalcUI(MyCalc)) {
//...
}

Passing arguments to a class

I am trying to create a mixin for a Polymer 2.0 components (class based syntax) with something like below. Is there a way to pass the options to the class the way I am doing it right now?
Polymer element definition with the mixin:
class PodcastListView extends PolymerApolloBehavior(Polymer.Element, myOptions) {
//some code
}
The mixin class:
export const PolymerApolloBehavior = (superclass, options) => class extends superclass {
constructor() {
console.log(options);
}
}
I can suggest you function-wrapper for your PodcastListView class that will makes a new one with new behavior. It will look something like this:
export const PolymerApolloBehavior = (original, options) => {
let result = Object.assign({}, original)
const originalConstructor = result.prototype.constructor
result.prototype.constructor = () => {
console.log(options) // your behavior
originalConstructor() // original constructor
}
// any changes of class
return result
}
And now you can apply your new behavior:
export default PolymerApolloBehavior(PodcastListView, {behaviorOptions})

Dynamically inherit from instance of the class in JavaScript

I am kind of struggling with inheritance in JavaScript. Let's say I have a following class:
class Parent {
constructor({ name, someOtherStuff } = {}) {
this.name = name;
this.someOtherStuff = someOtherStuff;
}
someMethod() {
// ...
}
}
and I would like to create a decorator that would allow me to do following:
#parent({
name: 'foo',
someOtherStuff: 'bar'
})
class MyClass extends Component {
myMethod() {
// ...
}
}
const instance = new MyClass();
// Those tests must pass
expect(instance.someMethod).toBeFunction();
expect(instance.name).toEqual('foo');
expect(instance.someOtherStuff).toEqual('bar');
expect(instance.myMethod).toBeFunction();
expect(instance instanceof Parent).toBe(true);
expect(instance instanceof MyClass).toBe(true);
Is there a way to create such decorator? I tried multiple solutions, but none of them really satisfies all the tests.
const parent = (...args) => (Target) => {
// Target corresponds to MyClass
const parent = new Parent(...args);
// ...
};
lodash is allowed.
Why use decorators? You can just extend parent class
class MyClass extends Parent {
constructor() {
super({name: 'foo', someOtherStuff: 'bar'});
}
}
You can use decorators to create a new class that inherits, apply some mixins, and go from there. JS classes don't have mutliple inheritance, so you can't do this directly, but you can combine the two manually or create a proxy that will do what you want.
I've been using wrapper classes for a decorator-based DI library by returning a class like so:
static wrapClass(target, {hook = noop} = {}) {
return class wrapper extends target {
static get wrappedClass() {
return target;
}
constructor(...args) {
super(...Injector.fromParams(args).getDependencies(wrapper).concat(args));
}
}
}
The decorator is really returning a new constructor with closure over the original, but that's enough for most purposes.

Categories

Resources