ES6 How to handle static property of sub class in super class - javascript

class Line extends Shape {
static howToDrag = { point: 0, update: 'xy' }
static config = { color: 'white' }
render() {
console.log('render based on this.config', this.config)
}
}
class Shape {
constructor({ config }) {
this.constructor.howToDrag = expandProperty1(this.constructor.howToDrag)
this.config = config || this.constructor.config
}
drag() {
console.log('drag based on this.constructor.howToDrag ', this.constructor.howToDrag)
}
}
function expandProperty1({ point, update }) {
if (typeof update === 'string') {
return {
point,
update: {
[point]: update
}
}
}
}
Here is the code, If you can understand what I am doing, skip the following explanation and provided some suggestions on what is the better way to make use of OOP because the current implementation is pretty wired from my perspective.
Check out http://canvas-chart.herokuapp.com/ to see what I am doing and I will explain the code above.
Let us talk about the static property config first. config should belong to each instance, i.e. each instance should have its own config, why I put it as static? The reason is that there is some common logic related to config, i.e. it should be the value passed from the constructor, otherwise, it has the default value. By setting the config as static, I am able to implement such common logic in 'Base' class, rather than write it over and over again in each Derived class
The other concern is howToDrag, it is a real static.However the static is used by base class but defined in the derived class, is it a normal thing? What is more, the static property in the derived class is mutated in the base class.
The current make logical sense but I always wonder if it is the way to do OOP because it relies on the fact this.constructor is base class point to the derived class.

Related

Private methods in React service function component

I'm a bit new to React and I'm currently developing a Proyect which has a service layer. To do so, I created a function component which would have the methods as variables:
const CustomComponent = () => {
method1 = () => {...},
method2 = () => {...},
method3 = () => {...}
}
export default CustomComponent;
This component would then be imported to the component that will use it.
To make my architecture as clean as possible, I wanted to make some of the methods private. However, as you may already know, that is not possible to do in the solution I proposed. Do hoy have an idea on how to achieve this, or maybe there is a convention to make a service layer I'm not aware of?
Thank you so much in advance!
The architecture which I find particularly clean and maintainable is one where you split off logic from presentation into two files like this:
Service layer (ts):
export class Service implements ServiceInterface {
constructor(private instanceVariable: string = "foo") { }
private methodOne(): string {
return this.instanceVariable
}
public methodTwo(argumentVariable: string): string {
const importantString = this.methodOne();
return importantString + argumentVariable;
}
}
interface ServiceInterface {
methodTwo(argumentVariable: string): string;
}
export default new Service();
Service layer (js):
export class Service {
instanceVariable;
constructor(contructorArgument) {
this.instanceVariable = contructorArgument;
}
methodOne() {
return this.instanceVariable
}
methodTwo(argumentVariable) {
const importantString = this.methodOne();
return importantString + argumentVariable;
}
}
export default new Service();
Presentation layer:
import Service from "./service.ts";
const FunctionalComponent = () => {
const [localState, setLocalState] = useState(localStateInit);
return (
<>
<div>{Service.methodTwo("bar")}</div>
</>
)
}
Few things happen here (mostly regarding ts implementation).
Keep component's service and presentation layers in separate files.
Use an interface to describe the service class and its methods. This will help to work with your service layer in your component as you'll get Typescript's IntelliSense.
For this example I'm exporting an instance of the service as default export from its file. This gives you a cleaner API in your component's file, where you can call methods without having to "pollute" component file with instance creation. This has at least the following two drawbacks:
you mostly lose ability to work nicely with static class members
preconfigured instance variable (initiated as a private member in constructor) means its value cannot be replaced in testing.
If any of above are a no go, then clean up the constructor, export just the class itself and instantiate it as required in component file.
I'm also exporting the class itself. This is for testing purposes. In testing you want to be able to swap out arguments passed into class' constructor and you need to have class definition to do that.
You'll notice the shorthand notation for declaring and instantiating a private class variable in the constructor: private instanceVariable: string = "foo". This is equivalent to something like this:
class Service {
private instanceVariable: string;
constructor(constructorArgument: string) {
this.instanceVariable = constructorArgument;
}
Such notation is particularly nice when used with dependency injection.
Overall, this setup will help you with unit testing logic in your service layer, as you can test it like any other class. This comes particularly handy when there's a lot of conditional rendering logic.
Let me know if this is what you've been looking for. Maybe we could tailor it better for your use case.

Why this works in JavaScript but TypeScript think it is invalid?

I tried to create a base class that has a static utility method to construct the class, which uses return new this(). But it doesn't work when I have a subclass extending it, because that utility method returns the base class instead of subclass, which is not the same as JavaScript.
Minimal Example:
class Base {
static create() {
return new this()
}
}
class Sub extends Base {
fn() {
return this
}
}
Sub.create().fn() // TypeScript error, JavaScript ok
Base.create().fn() // TypeScript error, JavaScript error
TypeScript Playground
It's because TypeScript doesn't have polymorphic this types for static methods. There's a longstanding open issue, microsoft/TypeScript#5863, asking for such a feature. For now it's not part of the language, but luckily comments on that issue describe a workaround that should get you the behavior you're looking for:
class Base {
static create<T extends Base>(this: new () => T) {
return new this()
}
}
Here we are making create() a generic method with a parameter of type T extends Base that can only be called on an object whose this context is a zero-arg constructor that returns a value of type T.
Therefore the compiler knows that Sub.create() will return a Sub and that Base.create() will return a Base:
Sub.create().fn() // okay
Base.create().fn() // Property 'fn' does not exist on type 'Base'
This also has the advantage over your existing code in that the following is an error now:
class Oops extends Base {
x: string;
constructor(x: string) {
super();
this.x = x.toUpperCase();
}
}
Oops.create(); // error!
The Oops class does not have a zero-arg constructor, and so the implementation of Oops.create() will end up calling new Oops() without the required parameter and cause a runtime error when it tries to call toUpperCase() on undefined.
Okay, hope that helps; good luck!
Playground link to code

Type of static subclass method types in Typescript

In Typescript I'm trying to get part of the type of a static getter class.
Here's a concrete example, where I'm trying to figure out how to get CSSTypes:
class View {
static get css (): Record<string, any> {
return {}
}
get jss (): CSSTypes {
/* return JSS-created CSS class names.
Equivalent in type (but not in practice, since we defer to the JSS library) to:
return Object.assign({},
...Object.keys(this.constructor.css).map(k => {[k]: `${k}-css-class-name`})
)
*/
}
}
I want CSSTypes to be a map of string keys of static get css to another string.
class WebComponent extends View {
static get css () {
return {
list: {
display: 'grid'
}
}
}
}
Here's what we get from the console:
>>> wc = new WebComponent()
// wc.jss should have one key, `list`
>>> wc.jss.list // === something like 'WebComponent__list-0-0-1'
If I were to take a stab at what we'd need, it'd look something like (which obviously doesn't work but kind of illustrates the idea):
class View {
...
get jss: Record<keyof typeof this.constructor.css, string> {...}
}
Context
For this example I'm using JSS with Knockout/TKO and we have hundreds of web-components, and since the documentation of what CSS variables exist from the jss getter is present (as the keys that are returned from static get css) we're hoping there's some magic Typescript that can figure it out without having to duplicate anything.
We use this.jss many thousands of times, so adding a type to that would be challenging, so we are hoping that we can add a type to get jss. Ideally we'd only modify the super class getter for jss. The jss getter is never modified i.e. exists only in the super class, but static get css is specific to every component.
To save memory and increase performance we (generally) have only one JSS style object per class type, hence static css.
This seems outside the scope of what Typescript ordinarily documents, and well outside my competence with it, but I'm hoping it's achievable since it theoretically seems possible.

Mixins as utility library in Polymer 2.0

I am working in web application project which is made in Polymer 2.0, All Custom elements extends some Mixins. Some of those Mixins just provide utility functions to custom elements just like Date Time Utility functions or any Math related functions. My question is whether to use mixins & extends them to custom elements or just wrap them in plain java-script file and load that java-script file on index.html or entry point of application and use as global scope just like we use lodashjs or underscore.js.
The problem i find with Mixins is it always get applied to prototype chain of each custom element class object, So i end up those same utility methods with each custom element of my application.
Please suggest me best approach for Utilities in Polymer related apps.
That is a rather tough question to answer properly... as the real answer is just "both are valid solutions - it depends on your use case". Which I guess is not helping too much. So let's give you some context.
For real utilities, it is probably best to put them in a class as a static function.
class MathHelper {
static addOne(value) {
return value + 1;
}
}
class ElOne extends HTMLElement {
connectedCallback() {
this.innerHTML = `5 + 1 = ${MathHelper.addOne(5)}`;
}
}
customElements.define('el-one', ElOne);
<el-one></el-one>
Mixins, on the other hand, can serve a similar need but should be more "connected" to the Element they are used on.
(Sorry for the terrible example but from the top of my head I could not come up with something better)
const MathMixin = superclass => class extends superclass {
addOneTo(prop) {
this[prop] += 1;
}
}
class ElTwo extends MathMixin(HTMLElement) {
constructor() {
super();
this.number = 5;
}
connectedCallback() {
this.addOneTo('number');
this.innerHTML = `5 + 1 = ${this.number}`;
}
}
customElements.define('el-two', ElTwo);
<el-two></el-two>
Conclusion:
Class:
if possible use it
maybe even use static functions
it's easier to test and to maintain
Mixin:
if you need to enhance another class e.g. to allow
myElemenet.changeLanguage('de'), ...
if you need any other state of the class
I hope this clears it up at least a bit. If you have more concrete examples what needs to be implemented we could propose a recommended way of implementation.

What is the correct way to pass a large number of attributes to a parent constructor if it's possible for an attribute to need a reference to `this`?

I have a general paradigm from which I'd like to create objects. Currently, I have it written in such a way that the parent class builds the paradigm from a configuration object given to it by the subclass. The configuration object defines all of the class' characteristics, and the class is also able to overload any methods that need to be slightly different from one implementation of the paradigm to the next.
This approach is convenient as it allows me to:
avoid a long list of parameters for the constructor,
prevent the subclass from needing to define the attributes in any particular order, and
prevent the subclass from needing to explicitly create instances that the parent constructor can imply from the configuration.
As much as I liked the cleanliness and flexibility of sending this configuration object to super(), I've found a glaring limitation to this approach:
I cannot make a reference to this until super has completed.
class Paradigm {
constructor(config) {
this.name = config.name;
this.relationships = config.relationships;
}
}
class Instance extends Paradigm {
constructor(name) {
super({
name: name,
relationships: {
children: {
foo: new Foo(this) // Error: `this` referenced before `super`
}
}
});
}
}
class Foo {
constructor(parent) {
this.parent = parent;
}
}
NOTE: The configuration object is typically significantly larger than this, which is why I'm using it at all.
I can imagine there are many ways to approach this, but I'm most interested in what would be the correct way.
Should I be using functions in place of statements that require a reference to this and only execute them when they are needed?
Should I keep the same approach, but simply replace super({...}) with this.build({...})?
Should I abandon the idea of the configuration object altogether and instead take a different approach?
The fact that this is needed before super often suggests that a constructor contains logic that should be moved to separate method.
In some cases some workarounds are possible, as long as they don't cause problems. E.g., a part of configuration that requires this can be instantiated later:
constructor(name) {
const config = {
name: name,
relationships: {
children: {};
}
};
super(config);
config.relationships.children.foo = new Foo(this);
}
Of course, this presumes that relationships is just stored as a reference and isn't processed on construction.
In more severe cases class hierarchy (Instance and Paradigm) may be required to be converted to ES5 classes, since ES6 classes can extend regular functions but not vice versa.
A possible way would be to change your Paradigma to this:
class Paradigm {
constructor(config) {
Object.assign(this, config(this));
}
}
So that you can do:
class Instance extends Paradigm {
constructor(name) {
super((context) => ({
name: name,
relationships: {
children: {
foo: new Foo(context)
}
}
}));
}
}
But actually this is a bad way of solving it...
Another way could be to do it the other way round:
const Paradigma = cls => class Paradigma extends cls {
constructor(...args){
super(config => setTimeout(() => Object.assign(this, config), 1), ...args);
}
};
So you can do:
const Instance = Paradigma(class {
constructor(build, name){
build({name});
}
});
new Instance("Test");

Categories