Javascript - "Compound" Class Inheritance - javascript

Not to be confused with inheriting from multiple classes.
Suppose there's a class, Player, with its own methods and properties:
class Player {
//...
}
Next, suppose there's a class which extends from Player, with its own special properties
class SpecificPlayer extends Player {
constructor() {
super();
this.foo = "bar";
}
}
Now suppose there's another class, Core, with a property of type Player[] (types are defined through JSDoc-style comments in VSCode), and a method which returns a portion of that array.
class Core {
constructor(players) {
/** #type {Player[]} */
this.players = players;
}
getSomePlayers() {
return this.players.filter(...);
}
}
Finally, let there be a class which extends from Core, with a constructor and method as follows:
class SpecificClass extends Core {
/** #param {SpecifcPlayer[]} players */
constructor(players) {
super(players);
}
doThing() {
console.log(this.players[0].foo);
this.getSomePlayers().forEach(p => console.log(p.foo));
}
}
Now, if the following main body code is ran:
const SpecificInstance = new SpecificClass([new SpecificPlayer(), new SpecificPlayer()]);
SpecificInstance.doThing();
Everything is fine and dandy -- "bar" is printed successfully for all log statements.
However, when writing the code (specifically the body of doThing), VSCode Intellisense would only recognize this.players as a type of Player, meaning it wouldn't recognize the property foo. How would I go about writing the classes such that the VSCode autocomplete/Intellisense recognizes the correct type of this.players, and each method in Core which uses this.players also returns the correct type of SpecificPlayer? Realistically, there are several classes extending from Core, some with their own special variation on Player. As such, the types returned should be that which was provided originally to the constructor.
(Or is there a better way to organize/format my code?)

Related

Code control couldn't go inside the child class constructor in javascript

I have been learning squish to automate a desktop application and I'm using Javascript at language to write my tests.
So I have these three following classes in my code : Actions, Button and AppElements. And AppElement class creates an instance of Button elements which is inheriting Actions class.
import * as names from 'names.js';
class Actions{
constructor(appObject){
this.object=(appObject);
}
doubleclick(){
doubleClick(waitForObjectExists(this.object));
}
}
class Button extends Actions{
constructor(anElement){
this.object=anElement;
super(anElement);
}
}
class AppElements {
get dashboardButton() { return new Button(names.contentButtonDashboardWcPushButton);}
}
export
{
Actions,
AppElements,
Button
};
import * as names from 'names.js';
function main() {
// attach the desktop application
let dash= new test.AppElements();
dash.dashboardButton.doubleclick();
}
But when the property dashboardButton() is being called from the test script(from the main function) then it first goes to line constructor(appObject){ but does not enter in the constructor block and gives following error
error Detail Attempted to access uninitialized this value. Forgot to call super() in derived class?
And because of that the Actions class can never be called and I cannot access any methods of the Actions class.
Would it be great if i could get some help to fix the problem
Here it says...
When used in a constructor, the super keyword appears alone and must be used before the this keyword is used. The super keyword can also be used to call functions on a parent object.
So try:
class Button extends Actions {
constructor(anElement) {
super(anElement);
this.object = anElement;
}
}
Related information:
JavaScript extensions in Squish 6.6
Super

ES6 code completion (Intellisense) with composition in VSCode

I am trying to get code completion to work when nesting classes in es6 using the following syntax: class Dog extends FoodMixin(Animal). The first implementation works fine, giving me auto completion for both FoodMixin and Animal. However, if I nest it one deeper, or apply a second mixin, the auto completion stops. For instance: class Dog extends OtherMixin(FoodMixin(Animal)) will lose the code completion for the FoodMixin class.
Is there a way I can have intellisense work for both OtherMixin and FoodMixin?
Simple test code:
const FoodMixin = superclass => class extends superclass {
eat() {
console.log("Eating");
}
};
const OtherMixin = superclass => class extends superclass {
test() {
console.log("Hello");
}
};
class Animal {
}
class Dog extends OtherMixin(FoodMixin(Animal)){
}
const dog = new Dog();
dog.test(); //INTELLISENSE WORKS
dog.eat(); //INTELLISENSE DOES NOT WORK
VS Code's JavaScript IntelliSense will not be able to understand very dynamic code like that example. The tutorial you linked to that provided this code is being too clever for its own (or really anyone's) good.
However you can sort of workaround VS Code's limitation by adding explicit type annotations for the types and interfaces used in the example using JSDoc:
/**
* #typedef {{ eat(): void }} Eater
*/
/**
* #typedef {{ test(): void }} Tester
*/
/**
* #type {Dog & Eater & Tester}
*/
const dog = new Dog();
dog.
The & is not standard JS Doc type syntax but an intersection type from TypeScript. (I am using it here because it mimics composition)

Not able to set properties in JavaScript class

I am simply setting properties in a class in JavaScript:
class BookingReports extends ReportsInterface{
var categoryID;
constructor() {
super();
}
init(CatID)
{
this.categoryID=CatID;
}
}
But JavaScript is not accepting variable at all and giving error "unexpected identifier".
I am not getting idea what is syntax error. Is it because of inheritance or super keyword? I even tried using binding with this whole declaration. But it is not working.
var categoryID is just out of place there. You don't declare variables at the class level; you declare variables within the constructor or methods.
In your case, you probably don't want to declare a variable at all; instead, create the property in your constructor:
class BookingReports extends ReportsInterface{
constructor() {
super();
this.categoryID = /*...some default value...*/;
}
init(CatID)
{
this.categoryID=CatID;
}
}
Side note: Having an init method doesn't make much sense; that's what the constructor is for. So:
class BookingReports extends ReportsInterface {
constructor(catID) {
super();
this.categoryID = catID;
}
}
At some point, the class fields proposal will reach Stage 4, at which point you will be able to "declare" properties at the class level if you want to:
// Valid if/when the class fields proposal advances
class BookingReports extends ReportsInterface {
categoryID; // Declares the property
constructor(catID) {
super();
this.categoryID = catID;
}
}
If you're initializing the property in the constructor, there's not a lot of point to that; but it can be useful for telling the reader (and JavaScript engine) the standard "shape" of your object up-front rather than making them (and it) analyze the constructor's code.

Implementing an interface in Javascript using flow-runtime

I am using flow-runtime plugin for babel to generate dynamically typechecked javascript code. The following is the workflow I am using
write static javascript code (with flow annotations)
compile this code using babel to convert flow annotations to typechecked code
run this compiled code in node.js
The following workflow gives me an ability to write typescript type code, but with type checking only where I want.
So, now that we understand what I am doing, let me explain what I am trying to achieve
I basically need to build a class called Interface, which will do exactly what it sounds like. This class will be extended by classes that are supposed to be interfaces, and then extended by other classes. Something like this :
class Interface() {
constructor() {
...
}
// interface superclass, supposed to be extended by all interfaces
// this class will provide all the utility methods required
// by an interface, such as validating the implementation of the
// interface, ...
validateInterfaceImplementation() {
...
}
}
// interface definition
class FooInterface extends Interface {
constructor() {
super();
...
}
}
// actual class, that will implement the "FooInterface" interface
class Foo extends FooInterface {
constructor() {
super();
...
}
}
Now, I want to enforce strict implementation of the FooInterface. That means that I want a way to define all the methods that the FooInterface interface expects to be implemented, and validation that all these methods have been implemented by the Foo class.
What I have tried looks something like this
// interface.js
// #flow-runtime
class Interface<T> {
constructor(t: T) {
(this: T); // let flow validate that the interface is implemented
}
}
// FooInterface.js
// #flow-runtime
type foInterface = {
bar(param: string): number;
}
class FooInterface extends Interface<fooInterface> {
constructor() {
super(fooInterface);
}
}
// Foo.js
// #flow-runtime
class Foo extends FooInterface {
}
new Foo(); // should throw an error, because the interface is not implemented
// (the function bar is not defined)
I am facing multiple problems with this approach
I am not sure how to implement the generic class Interface<T>. In think my implementation is incorrect, and the compiled babel code also throws an error, but I can't figure out how to do this.
I am not even sure whether this method will work or not, or whether this is the best way to approach this problem.
Any help would be welcome. Thanks in advance :)
As of flow-runtime 0.5.0 you can use Flow's implements keyword combined with Flow interfaces. I think this will give you what you want without having to create the concrete classes at all:
// #flow
// #flow-runtime
interface IPoint<T> {
x: T;
y: T;
}
interface IJSON {
toJSON (): any;
}
class Point implements IPoint<number>, IJSON {
x: number = 123;
y: number = 456;
toJSON () {
return {x: this.x, y: this.y};
}
}

ES6, access props set by the child from the parent's constructor

I am trying to set up a class hierarchy with ES6 classes.
All entities will inherit from single Base class. It will need to access properties exposed by the Child class in a generic manner. Like this:
class Base {
constructor() {
this.doSomethingWithProps();
}
doSomethingWithProps() {
console.log(Object.keys(this));
}
}
class Entity extends Base {
constructor() {
super();
this.key = "value";
}
}
Obviously, in the example above, Base class will not see the key prop set up by Entity. Ideally I would move the this assignment before super(), but that's not allowed. It would also be nice to be able to set properties before constructor, but AFAIK, that's not possible either.
The only solution I am left with is doing something like the following in each Entity:
class Base {
doSomethingWithProps() {
console.log(Object.keys(this));
}
}
class Entity extends Base {
constructor() {
super();
this.key = "value";
this.doSomethingWithProps();
}
}
However, besides being less than ideal, it will also create problems if I then want to inherit from Entity. doSomethingWithProps would then need to be able to detect if it's the "top-most" method in call hierarchy and only do its thing then. The only way to achieve that (that I can think of) would involve even more boilerplate.
Is there some solution I'm missing here? I'd be open to using a different OOP pattern if needed, although I'd like to stay as close as possible to native ES6 classes.
What you are trying to do is quite impossible. The parent initialisation always runs before the child initialisation, so it is imperative that the parent constructor does not rely on properties that might be overwritten by the child. This is a language-agnostic problem, btw.
The solution is to use parameters for the constructor, which can be modified in the child before they reach the parent code:
class Base {
constructor(key) {
this.key = key;
// do something with key
}
}
class Entity extends Base {
constructor() {
super("value");
}
}
console.log(new Entity);
or more generic
class Base {
constructor(props) {
this.doSomething(props);
Object.assign(this, props);
}
doSomething(props) {
return Object.keys(props);
}
}
class Entity extends Base {
constructor() {
super({key: "value"});
}
}
console.log(new Entity);
Also notice that constructors should always be pure. Their only purpose is to initialise the new instance from the arguments, and do nothing else like execute some side effects. A constructor should usually not need to call any (overwritable) instance methods (static methods are ok though).
So if you need to do something like that nonetheless when creating your instances, just don't do it in the constructor. Instead, call a method after using the constructor:
class Base {
log() {
console.log(Object.keys(this));
return this;
}
}
class Entity extends Base {
constructor() {
super();
this.key = "value";
}
}
var e = new Entity().log();
You can also abstract that out:
class Base {
static createAndLog(...args) {
var x = new this(...args);
x.log();
return x;
}
…
}
…
var e = Entity.createAndLog();
Depending on how complex you want to go, there's probably two routes you could go down.
1. Keep state in Base
If possible, pass the props you need from Entity into Base and maintain state there.
class Base {
constructor(props) {
for (var k in props) {
this[k] = props[k]
}
this.doSomethingWithProps();
}
doSomethingWithProps() {
console.log(Object.keys(this));
}
}
class Entity extends Base {
constructor(props) {
super(props);
}
}
let entity = new Entity({key: 'value'})
// ["key"]
2. Pass reference to Entity into Base
Note, I was incorrectly assuming separate instances of Entity.. this is overly redundant.
Not as simple, and will technically contain a self referencing key as Entity will inherit the child key from Base. This would definitely be considered a "wrong" approach, but I'm including it for the heck of it.
class Base {
constructor() {
this.child
}
registerChild(child) {
this.child = child
this.doSomethingWithProps()
}
doSomethingWithProps() {
console.log(Object.keys(this.child));
}
}
class Entity extends Base {
constructor() {
super();
this.key = 'value'
this.registerChild(this)
}
}
let entity = new Entity()
// ["key", "child"]
I think the observation here should be: avoid calling a method that relies on the object's state in the constructor, since constructors are meant to build that state.
Actually, what you suppose to ask is about js syntax, however, the root of the issue is the idea of oop
Consider what extends mean when you write:
Entity extends Base
It means Entity make use of something from Base, the keyword is extends rather than something like communicate, which means Base should NOT care about Entity at the first place. extends can also mean Entity has something which is not expected by Base, thus extends.
However, from my own point of view(correct me if I am wrong), there are some valid use cases in which parent class has some awareness of child class.
For example: put some common logic in Parent class to manipulate the property of Child, in such case, from the point of view of logic(not syntax), the child property should be static, i.e. belong to the whole class rather than a instance

Categories

Resources