Method substitution between objects - javascript

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

Related

How to extends abstract class from template in TypeScript?

I have an architecture like that and I want to extends my TemplateService with a template but actually I have an error on it. Is there a way to extends an abstract class from a template ?
// Error message
ReferenceError: Service is not defined
abstract class TemplateService<Service> extends Service
^
// My code
abstract class TemplateService<Service> extends Service { // Cannot find name 'Service'.
constructor() {
const name = "Hello world !";
super(name);
}
public getJob(): string {
return "Job";
}
protected getAge(): number {
return 25;
}
}
class ImportedService {
constructor(private _name: string) {}
public getName() {
return this._name;
}
}
class MyService extends TemplateService<ImportedService> {
constructor() {
super();
}
public fooMethod(): void {
...
}
}
It is a category error to write
abstract class TemplateService<Service> extends Service {}
TypeScript's static type system, including generic type parameters, is erased from the emitted JavaScript that actually runs. In the above, abstract and <Service> are part of the static type system, while the rest is valid JavaScript (at least for modern versions of JavaScript). Your code as written would be emitted to something like
class TemplateService extends Service {}
which hopefully shows the problem. Unless there happens to be a class constructor named Service in scope, this would be a runtime error. You can't subclass a type; you need to subclass a constructor value.
TypeScript has some support for subclassing an arbitrary constructor value via the concept of mixins. Instead of writing TemplateService as a class, you make it a class factory function that accepts a class constructor as an input. Conceptually it would look like
// don't write this, it's not valid TS
function TemplateService<C extends new (name: string) => object>(ctor: C) {
return class extends ctor { // error!
// ------> ~~~~~
constructor() {
const name = "Hello world !";
super(name);
}
public getJob(): string {
return "Job";
}
protected getAge(): number {
return 25;
}
};
}
And you'd use it like
class ImportedService {
constructor(private _name: string) { }
public getName() {
return this._name;
}
}
class MyService extends TemplateService(ImportedService) {
constructor() {
super();
}
public fooMethod(): void {
console.log(this.getName());
console.log(this.getJob());
console.log(this.getAge())
}
}
Unfortunately, TypeScript's mixin support assumes that you are going to just pass the constructor parameters through, so the above TemplateService implementation gives compiler errors. There are open issues about this, such as microsoft/TypeScript#37142. For now all I could say is to work around it by using several type assertions to trick the compiler into allowing the mixin and into to giving it the correct type.
For the particular example code here, it could look like:
function TemplateService<C extends new (name: string) => object>(ctor: C) {
const c = class extends (ctor as new (...args: any) => object) {
constructor() {
const name = "Hello world !";
super(name);
}
public getJob(): string {
return "Job";
}
protected getAge(): number {
return 25;
}
};
return c as {
new(): InstanceType<C> & InstanceType<typeof c>;
}
}
Now the compiler believes that TemplateService(ImportedService) is a no-arg constructor which produces instances of a combination (via intersection) of ImportedService and the type of the anonymous class expression with a getJob() method and protected getAge() method.
Playground link to code

Extending a TypeScript class with multiple generic parameters

i try to extend a React Component Class with generic Parameters but I cannot get it working. According to the TypeScript Docs and this TypeScript Class Generic Parameter Constraints, and this extending a class with a generic T I ended up in the following (not working) code:
Base Class:
class BaseView<P, S> extends React.Component<P, S> {
public constructor(props: P, state: S) {
super(props, state);
}
}
const mapStateToProps = store => {
console.log('store', store);
return {
store
};
};
function mapDispatchToProps(dispatch) {
console.log('dispatch', dispatch);
return {
};
}
export default connect(mapStateToProps, mapDispatchToProps)((BaseView as any));
export function subclass<P, S>(c): BaseView<P, S> {
return c;
}
The class which extends the Base Class:
I tried different ways, using the factory like this:
export class Documents extends subclass(BaseView)<DocumentsProperties, DocumentsState> {
// this leads into ""BaseView<{}, {}>" is no constructor function type
or without using the factory:
export class Documents extends BaseView<DocumentsProperties, DocumentsState> {
// no base constructor has the specified number of type arguments
I also tried out several other ways but i feel like i was programming for the first time of my life....I cannot get it running for multiple Parameters. What am I missing here?
greetings Messerbill

Add a method to an existing class in typescript?

I am working in an angular 2 cli project in which I have to create a definition of a plugin, because it doesn't exists its typed. This plugin depends of a main library that has already its own typed and it works.
Anyway,
I have two files
the main one with
LIBRARY TYPES FILE A
export class A extends B {
constructor(...);
methodX(): void;
}
And I would need to add a new method for my plugin so my class would be like
export class A extends B {
constructor(...);
methodX(): void;
methodY(): void;
}
The point is that I need to add it in a separate file. The problem is adding a method to an existent class without creating a new one
If I put
PLUGIN TYPES FILE B
export class A extends B {
constructor(...);
methodX(): void;
}
or
PLUGIN TYPES FILE B
export class A extends B {
constructor(...);
methodX(): void;
methodY(): void;
}
It doesn't work, does anyone how can I achieve overwriting a class or extending it with a new method that?
Thanks
The "Declaration Merging > Module Augmentation" section from the TypeScript docs seems to offer the solution:
https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
In your case, if class A is exported from file1.ts, and you want to add methodY() to that class within a different module file2.ts, then try this:
//// file1.ts
export class A extends B {
constructor(...);
methodX(): void;
}
//// file2.ts
import { A } from "./file1";
declare module "./file1" {
interface A {
methodY(): void;
}
}
A.prototype.methodY = function() {}
You could do it by making an interface with the new method and modifying the prototype.
Something like this:
class B { }
class A extends B {
constructor() {
super();
}
methodX(): void { };
methodY(): void { };
}
interface B {
newMethod(): void;
}
B.prototype.newMethod = function () { console.log('a') };
This allows you do have proper typing when doing.
new A().newMethod();
I made a playground example here.
You can do it directly as A.prototype.functionName = function(){...}
Here is a plunker: http://plnkr.co/edit/6KrhTCLTHw9wjMTSI7NH?p=preview

How to pass a string or class to a method to create instance

I am to use the following method, it works by passing a type to it such as obj.addComponent(MyClass). This works just fine.
I tried to modify the type parameter by adding | string to it, but it now gives me errors saying:
Cannot use 'new' with an expression whose type lacks a call or construct signature.
Is there anyway for me to modify this so that I can pass either a Class name in or a string version of the class?
Here is what I have that doesn't work:
public addComponent<T extends Component>(type: ComponentType<T> | string): T {
let comp;
comp = new type() as T;
comp.name = comp.constructor.name;
}
Here are its dependencies:
class Component extends Obj {
}
interface ComponentType<T extends Component> {
new(): T;
}
I have tried using Object.create(), which works fine, but then I get a new error:
Uncaught TypeError: Cannot assign to read only property 'name' of object '[object Object]'
Edit:
In the end I would like to be able to pass the following to addComponent:
obj.addComponent(MyClass);
Or
obj.addComponent("MyClass");
There's no way to get the class using a name in javascript, it doesn't have something similar to the java ClassLoader.
You can get around that by creating your own mechanism, and there are probably many ways to do so, but here are 3 options.
(1) Maintain a registry for your component classes:
const REGISTRY: { [name: string]: ComponentType<Component> } = {};
class Component {}
class MyComponent1 extends Component {}
REGISTRY["MyComponent1"] = MyComponent1;
class MyComponent2 extends Component {}
REGISTRY["MyComponent2"] = MyComponent2;
type ComponentType<T extends Component> = {
new(): T;
}
function factory<T extends Component>(type: ComponentType<T> | string): T {
return typeof type === "string" ?
new REGISTRY[type]() as T:
new type();
}
(code in playground)
If you go with this approach then I suggest to make the REGISTRY an object that holds the collection, that way you can add the ctor only and get the name from that.
There's a variant for this and that's to use a decorator:
function register(constructor: typeof Component) {
REGISTRY[(constructor as any).name] = constructor;
}
#register
class MyComponent1 extends Component {}
#register
class MyComponent2 extends Component {}
(code in playground)
(2) Wrap the components in a namespace (As #Shilly suggested in a comment):
namespace components {
export class Component {}
export class MyComponent1 extends Component {}
export class MyComponent2 extends Component {}
export type ComponentType<T extends Component> = {
new(): T;
}
export function forName(name: string): ComponentType<Component> {
if (this[name] && this[name].prototype instanceof Component) {
return this[name];
}
}
}
function factory<T extends components.Component>(type: components.ComponentType<T> | string): T {
return typeof type === "string" ?
new (components.forName(type))() as T:
new type();
}
(code in playground)
If you're going with this approach then you need to make sure that all the component classes are exported.
(3) Use eval
class Component {}
class MyComponent1 extends Component {}
class MyComponent2 extends Component {}
type ComponentType<T extends Component> = {
new(): T;
}
function factory<T extends Component>(type: ComponentType<T> | string): T {
return typeof type === "string" ?
new (eval(type))() as T:
new type();
}
(code in playground)
This isn't a recommended approach, and you can read all about the cons in using eval in a lot of places.
But it's still an option so I'm listing it.
There is a way to instantiate classes by their name as String if they are in a namespace :
var variableName: any = new YourNamespace[YourClassNameString](ClassParameters);
For exmaple, this should work :
namespace myNamespace {
export class myClass {
example() {
return true;
}
}
}
var newClass: any = new myNamespace["myClass"](); // <- This loads the class A.
newClass.example();
This will instantiate the class myClass using the string "myClass".
Thus, to come back to your situation, I think this will work :
namespace myNamespace {
// The dependencies you defined
export class Component {
}
export interface ComponentType<T extends Component> {
new(): T;
}
// Just a class to contain the method's code
export class Example {
public addComponent<T extends Component>(type: ComponentType<T> | string): T {
let result: T;
if (typeof type === "string") {
result = new myNamespace[type]();
} else {
result = new type();
}
return result;
}
}
}
Then, you'll be able to do this :
let stringToLoad = "Component";
let classToLoad = Component;
let example = new Example();
let result1: Component = example.addComponent(stringToLoad);
let result2: Component = example.addComponent(classToLoad);
Playground version with code + test : here

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