i want to extend from one class but delete some property - javascript

I have some class for crud API service class
I want to extend it but spear few properties
for exam this is my class
class Parent {
public propertyToKeep: any;
public propertyToDelete: any;
constructor() { }
}
this is the child class
class Child extends Parent {
constructor() {
super();
}
}
Another file
where I don't want to see and get access to
export class comeComponent {
constructor(private child: Child) {
this.child.propertyToKeep // work
this.child.propertyToDelete // error and I can't even see it
}
}

I just came across the same use case as you and here's how I did it:
const Omit = <T, K extends keyof T>(Class: new () => T, keys: K[]): new () => Omit<T, typeof keys[number]> => Class;
Then you can use it like so :
class Child extends Omit(Parent, ['propertyToDelete']) {}
As you can see child only has one property now (it also works with methods).
The package from #nestjs/swagger has some nice helpers if you're dealing with a NestJS API. Their implementation is more complex so I guess they are keeping other stuff like their own property decorators (I am pretty new to Typescript so maybe I miss the point of all they are doing).
P.S: French guy tried to answer for first time with imperfect English so please be kind ^^

Here is one way to do it:
class Parent {
propertyToKeep = 'hi';
propertyToDelete = 'bye';
constructor() {}
}
class Child extends Parent {
constructor() {
super();
delete this.propertyToDelete;
}
}
const myObject = new Child();
console.log(myObject);
/* OUTPUT:
{
"propertyToKeep": "hi"
}
*/

You need to use Object.defineProperty function, for make a restriction in descriptor, enumerable to false and getter, setter with specific condition, here a complete example:
//A small example of how to make an invisible property in Child class.
class Parent{
constructor(){
this.propertyToKeep = "Visible";
this.propertyToDelete = "Not visible in subclass child";
}
}
Object.defineProperty(Parent.prototype, "propertyToDelete", {enumerable: false,
configurable: true,
get: function(){
if(!(this instanceof Child)){
return this._propertyToDelete;
}
},
set: function(v){
if(!(this instanceof Child)){
this._propertyToDelete = v;
}
}});
Object.freeze(Parent.prototype);
class Child extends Parent {
constructor() {
super();
}
}
//console.log(Child.prototype);
let chd = new Child();
console.log("Child: --------------------------------");
console.log(chd);
console.log(chd.propertyToDelete); //Undefined
console.log("Parent: -------------------------------");
let prt = new Parent();
console.log(prt);
console.log(prt.propertyToDelete); //"Not visible in subclass child"
/*let chdObj = Object.getOwnPropertyDescriptors(Child.prototype);
console.log(chdObj);*/
class SomeComponent{
#child;
constructor(child) {
this.#child = child;
console.log(this.#child); //{propertyToKeep: "Visible"}
console.log(this.#child.propertyToKeep /*work*/);
console.log(this.#child.propertyToDelete /*undefined*/);
}
}
//Now i will invoke SomeComponent
console.log("SomeComponent: -------------------------");
let sc = new SomeComponent(new Child());

That's not possible. If you declare an attribute in a parent class, you can't restrict his visibility in a child class.
From the model design point of view, that doesn't make sense as well. The problem you are exposing here indicates that your class hierarchy is not well designed and you must rethink and redesign it.

Related

Calling parent constructor in javascript

Wonder if anyone can help?
I'm trying to call a parent's constructor or at a minimum get a reference to the parent class in some way without hardcoding anything.
class A {
static foo(options) {
parent::__construct(options); <- this is how you would get the parent in php
}
}
class B extends A {
}
Is this possible?
In a javascript class (and OOP in general), a static method is not part of an instance and therefore the object it resides in does not have a constructor.
You should avoid using static method for this sort of thing and use a standard constructor and call super() to call the parent constructor.
class A {
constructor(options) {
console.log('Options are:');
console.log(options);
}
}
class B extends A {
constructor(options) {
super(options);
}
}
const item = new B({item1: 'abc'});
Further reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
You can use super() to call parent constructor
class A {
constructor() {
console.log('I\'m parent ');
}
foo(){
console.log('Class A: Called foo');
}
}
class B extends A {
constructor() {
super();
}
foo(){
super.foo()
}
}
const b = new B();
b.foo();

typescript: member variables from super class's constructor

The following code will alert undefined
class Parent {
field: string
constructor() {
alert(this.field)
}
}
class Child extends Parent {
field = 'child'
}
new Child() #=> undefined
whereas, the following alerts 'child' as expected
class Parent {
field: string
constructor() {
alert(this.field)
}
}
class Child extends Parent {
field = 'child'
constructor() {
// without referencing this.field before super(), this.field stays undefiend
this.field
super()
}
}
new Child() #=> 'child'
Is there any ways to accomplish the following conditions?
omit the whole Child's constructor declaration like the first example
grab the member variable in Child class?
What jumps to mind is:
class Parent {
constructor(public field: string) {
alert(this.field)
}
}
class Child extends Parent {
constructor() {
super('child');
}
}
new Child() #=> 'child'
This doesn't meet your conditions but I feel it is fairly compact.
Well you could defer the property access to a micortask:
class Parent {
field: string
constructor() {
Promise.resolve().then(() => {
alert(this.field)
};
}
}
But while that fullfills your conditions, it is ... still the wrong approach. Pass field as a constructor argument, as other answers show.
There's no way to accomplish your conditions, i'm pretty sure.
Grabbing the member variable in sub class happens after the base class constructor runs, and calling super() must be the first statement in a sub class constructor.
class Child extends Parent {
field = 'child';
}
// the above is equal to:
class Child extends Parent {
constructor(){ super(); this.field = 'child'; }
}
And this would cause error:
class Child extends Parent {
constructor(){
this.field = 'child'; // ERROR!
super();
}
}

How to create a decorator that adds a single function to the component class?

I should preface this by saying I understand very little about es7 decorators. Basically what I want is a decorator called #model that adds a function to a component called model. So for example I'd call it like
#model class FooBar extends Component { }
and then the FooBar class would now have the model function.
Here's what I tried:
Model.js
export default function reactModelFactory( ctx ){
return (key)=>{
return {
onChange: (e)=>ctx.setState({[key]:e.target.value}),
value: ctx.state[key],
name: key
};
};
};
function modelDecorator() {
return function(ctx){
return class extends ctx{
constructor(...args){
super(...args);
this.model = reactModelFactory(this);
}
}
}
}
export { modelDecorator as model };
Login.js
import React,{PureComponent} from 'react';
import {model} from './Model';
#model class Login extends PureComponent{}
React throws with error message:
TypeError: Super expression must either be null or a function, not object
I have no idea what this means. I'm looking for some help in getting my decorator working, and a bonus would be to understand the concept of decorators at all.
To add to #dfsq's answer (I'm assuming it does what you want), you can go a step further in terms of interface performance by adding model() to the prototype instead of to each instance like this:
export default function reactModelFactory() {
return function model (key) {
return {
onChange: (e) => this.setState({ [key]: e.target.value }),
value: this.state[key],
name: key
};
};
};
function modelDecorator(Class) {
Object.defineProperty(Class.prototype, 'model', {
value: reactModelFactory(),
configurable: true,
writable: true
});
return Class;
}
This is much better for performance as it causes the decorator to modify the existing class's prototype a single time with the model member method, rather than attaching a scoped copy of the model method within the anonymous extended class's constructor each time a new instance is constructed.
To clarify, this means that in #dfsq's answer, reactModelFactory() is invoked each time a new instance is constructed, while in this answer, reactModelFactory() is only invoked a single time when the decorator is activated on the class.
The reason I used configurable and writable in the property descriptor is because that's how the class { } syntax natively defines member methods on the prototype:
class Dummy {
dummy () {}
}
let {
configurable,
writable,
enumerable
} = Object.getOwnPropertyDescriptor(Dummy.prototype, 'dummy');
console.log('configurable', configurable);
console.log('enumerable', enumerable);
console.log('writable', writable);
Your model decorator should not return new function. ctx will be passed to modelDecorator itself. So you really just need to return newly extended class from it:
function modelDecorator(ctx) {
return class extends ctx {
constructor(...args) {
super(...args);
this.model = reactModelFactory(this);
}
}
}
Note, that the syntax you tried would work if your decorator was supposed to be used like this (Angular style decorators):
#model({ modelName: 'user' })
class Login extends PureComponent {}
Then you would need extra closure to keep passed parameters into your decorator:
function modelDecorator({ modelName }) {
return (ctx) => {
console.log('model name', modelName)
return class extends ctx {
constructor(...args) {
super(...args);
this.model = reactModelFactory(this);
}
}
}
}

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.

Typescript: How to extend two classes?

I want to save my time and reuse common code across classes that extend PIXI classes (a 2d webGl renderer library).
Object Interfaces:
module Game.Core {
export interface IObject {}
export interface IManagedObject extends IObject{
getKeyInManager(key: string): string;
setKeyInManager(key: string): IObject;
}
}
My issue is that the code inside getKeyInManager and setKeyInManager will not change and I want to reuse it, not to duplicate it, here is the implementation:
export class ObjectThatShouldAlsoBeExtended{
private _keyInManager: string;
public getKeyInManager(key: string): string{
return this._keyInManager;
}
public setKeyInManager(key: string): DisplayObject{
this._keyInManager = key;
return this;
}
}
What I want to do is to automatically add, through a Manager.add(), the key used in the manager to reference the object inside the object itself in its property _keyInManager.
So, let's take an example with a Texture. Here goes the TextureManager
module Game.Managers {
export class TextureManager extends Game.Managers.Manager {
public createFromLocalImage(name: string, relativePath: string): Game.Core.Texture{
return this.add(name, Game.Core.Texture.fromImage("/" + relativePath)).get(name);
}
}
}
When I do this.add(), I want the Game.Managers.Manager add() method to call a method which would exist on the object returned by Game.Core.Texture.fromImage("/" + relativePath). This object, in this case would be a Texture:
module Game.Core {
// I must extend PIXI.Texture, but I need to inject the methods in IManagedObject.
export class Texture extends PIXI.Texture {
}
}
I know that IManagedObject is an interface and cannot contain implementation, but I don't know what to write to inject the class ObjectThatShouldAlsoBeExtended inside my Texture class. Knowing that the same process would be required for Sprite, TilingSprite, Layer and more.
I need experienced TypeScript feedback/advice here, it must be possible to do it, but not by multiple extends since only one is possible at the time, I didn't find any other solution.
There is a little known feature in TypeScript that allows you to use Mixins to create re-usable small objects. You can compose these into larger objects using multiple inheritance (multiple inheritance is not allowed for classes, but it is allowed for mixins - which are like interfaces with an associated implenentation).
More information on TypeScript Mixins
I think you could use this technique to share common components between many classes in your game and to re-use many of these components from a single class in your game:
Here is a quick Mixins demo... first, the flavours that you want to mix:
class CanEat {
public eat() {
alert('Munch Munch.');
}
}
class CanSleep {
sleep() {
alert('Zzzzzzz.');
}
}
Then the magic method for Mixin creation (you only need this once somewhere in your program...)
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') {
derivedCtor.prototype[name] = baseCtor.prototype[name];
}
});
});
}
And then you can create classes with multiple inheritance from mixin flavours:
class Being implements CanEat, CanSleep {
eat: () => void;
sleep: () => void;
}
applyMixins (Being, [CanEat, CanSleep]);
Note that there is no actual implementation in this class - just enough to make it pass the requirements of the "interfaces". But when we use this class - it all works.
var being = new Being();
// Zzzzzzz...
being.sleep();
I would suggest using the new mixins approach described there: https://blogs.msdn.microsoft.com/typescript/2017/02/22/announcing-typescript-2-2/
This approach is better, than the "applyMixins" approach described by Fenton, because the autocompiler would help you and show all the methods / properties from the both base and 2nd inheritance classes.
This approach might be checked on the TS Playground site.
Here is the implementation:
class MainClass {
testMainClass() {
alert("testMainClass");
}
}
const addSecondInheritance = (BaseClass: { new(...args) }) => {
return class extends BaseClass {
testSecondInheritance() {
alert("testSecondInheritance");
}
}
}
// Prepare the new class, which "inherits" 2 classes (MainClass and the cass declared in the addSecondInheritance method)
const SecondInheritanceClass = addSecondInheritance(MainClass);
// Create object from the new prepared class
const secondInheritanceObj = new SecondInheritanceClass();
secondInheritanceObj.testMainClass();
secondInheritanceObj.testSecondInheritance();
I found an up-to-date & unparalleled solution: https://www.npmjs.com/package/ts-mixer
You are welcome :)
TypeScript supports decorators, and using that feature plus a little library called typescript-mix you can use mixins to have multiple inheritance with just a couple of lines
// The following line is only for intellisense to work
interface Shopperholic extends Buyer, Transportable {}
class Shopperholic {
// The following line is where we "extend" from other 2 classes
#use( Buyer, Transportable ) this
price = 2000;
}
I think there is a much better approach, that allows for solid type-safety and scalability.
First declare interfaces that you want to implement on your target class:
interface IBar {
doBarThings(): void;
}
interface IBazz {
doBazzThings(): void;
}
class Foo implements IBar, IBazz {}
Now we have to add the implementation to the Foo class. We can use class mixins that also implements these interfaces:
class Base {}
type Constructor<I = Base> = new (...args: any[]) => I;
function Bar<T extends Constructor>(constructor: T = Base as any) {
return class extends constructor implements IBar {
public doBarThings() {
console.log("Do bar!");
}
};
}
function Bazz<T extends Constructor>(constructor: T = Base as any) {
return class extends constructor implements IBazz {
public doBazzThings() {
console.log("Do bazz!");
}
};
}
Extend the Foo class with the class mixins:
class Foo extends Bar(Bazz()) implements IBar, IBazz {
public doBarThings() {
super.doBarThings();
console.log("Override mixin");
}
}
const foo = new Foo();
foo.doBazzThings(); // Do bazz!
foo.doBarThings(); // Do bar! // Override mixin
Unfortunately typescript does not support multiple inheritance. Therefore there is no completely trivial answer, you will probably have to restructure your program
Here are a few suggestions:
If this additional class contains behaviour that many of your subclasses share, it makes sense to insert it into the class hierarchy, somewhere at the top. Maybe you could derive the common superclass of Sprite, Texture, Layer, ... from this class ? This would be a good choice, if you can find a good spot in the type hirarchy. But I would not recommend to just insert this class at a random point. Inheritance expresses an "Is a - relationship" e.g. a dog is an animal, a texture is an instance of this class. You would have to ask yourself, if this really models the relationship between the objects in your code. A logical inheritance tree is very valuable
If the additional class does not fit logically into the type hierarchy, you could use aggregation. That means that you add an instance variable of the type of this class to a common superclass of Sprite, Texture, Layer, ... Then you can access the variable with its getter/setter in all subclasses. This models a "Has a - relationship".
You could also convert your class into an interface. Then you could extend the interface with all your classes but would have to implement the methods correctly in each class. This means some code redundancy but in this case not much.
You have to decide for yourself which approach you like best. Personally I would recommend to convert the class to an interface.
One tip: Typescript offers properties, which are syntactic sugar for getters and setters. You might want to take a look at this: http://blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/
A very hacky solution would be to loop through the class you want to inherit from adding the functions one by one to the new parent class
class ChildA {
public static x = 5
}
class ChildB {
public static y = 6
}
class Parent {}
for (const property in ChildA) {
Parent[property] = ChildA[property]
}
for (const property in ChildB) {
Parent[property] = ChildB[property]
}
Parent.x
// 5
Parent.y
// 6
All properties of ChildA and ChildB can now be accessed from the Parent class, however they will not be recognised meaning that you will receive warnings such as Property 'x' does not exist on 'typeof Parent'
In design patterns there is a principle called "favouring composition over inheritance". It says instead of inheriting Class B from Class A ,put an instance of class A inside class B as a property and then you can use functionalities of class A inside class B.
You can see some examples of that here and here.
In my case I used concatenative inheritance.
Maybe for someone this way will be helpful:
class Sprite {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
class Plane extends Sprite {
fly(): string {
return 'I can Fly!'
}
}
class Enemy {
isEnemy = true;
}
class Player {
isPlayer = true;
}
// You can create factory functions to create new instances
const enemyPlane = Object.assign(new Plane(1, 1), new Enemy());
const playerPlane = Object.assign(new Plane(2, 2), new Player());
Also I recommend reading Eric Elliott's articles about js inheritance:
The Heart & Soul of Prototypal OO: Concatenative Inheritance
3 Different Kinds of Prototypal Inheritance
There are so many good answers here already, but i just want to show with an example that you can add additional functionality to the class being extended;
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') {
derivedCtor.prototype[name] = baseCtor.prototype[name];
}
});
});
}
class Class1 {
doWork() {
console.log('Working');
}
}
class Class2 {
sleep() {
console.log('Sleeping');
}
}
class FatClass implements Class1, Class2 {
doWork: () => void = () => { };
sleep: () => void = () => { };
x: number = 23;
private _z: number = 80;
get z(): number {
return this._z;
}
set z(newZ) {
this._z = newZ;
}
saySomething(y: string) {
console.log(`Just saying ${y}...`);
}
}
applyMixins(FatClass, [Class1, Class2]);
let fatClass = new FatClass();
fatClass.doWork();
fatClass.saySomething("nothing");
console.log(fatClass.x);
You can call Dynamic Inheritance or Class Factory.
type ClassConstructor<T> = {
new (...args: any[]): T;
};
interface IA {
a: string;
}
interface IB {
b: string;
}
interface IAB extends IA, IB {}
class EmptyClass {}
function GetA<T>(t: ClassConstructor<T> = EmptyClass as any) {
class A extends (t as any) implements IA {
a = 'Default value a';
}
return A as unknown as ClassConstructor<IA & T>;
}
function GetB<T>(t: ClassConstructor<T> = EmptyClass as any) {
class B extends (t as any) implements IB {
b = 'Default value b';
}
return B as unknown as ClassConstructor<IB & T>;
}
class C extends GetA<IB>(GetB()) implements IAB {}
Found a way:
export interface IsA {
aWork(): void
}
export interface IsB {
}
export class A implements IsA {
aWork() { }
}
export class B implements IsB {
bWork() { }
}
export interface IsAB extends A, B {}
export class IsAB {}
Then you can
export class C extends IsAB {}
or even
const c = new IsAB {}

Categories

Resources