1
votes

I am following this exemple =>

Typescript: How to extend two classes?

In My case, I want to extend a main class =>

export class MyClassOne {
   constructor(param1, param2)
}

and

MyClassTwo<F extends interface1, O extends interface2>{
        constructor(param3, param4)
}

So I tried the following

const addMyClassOneInheritance = (MyClassOne : { new(...args) }) => {
  return class extends MyClassOne {};
}
const MyFinalClass = addMyClassOneInheritance(MyClassTwo);
export class SynchMapComponent extends MyFinalClass{

it works but I don't know a way to set my generic types. because if I had anything within the <> I get an error

Value of type 'typeof MyClassTwo' is not callable. Did you mean to include 'new'?

3
@RannieAguilarPeralta yes thank you... its exactly the link i post in the first line.... I will edit the name then so at least even if people don't click they will know...Bobby
@Bobby could you provide a fuller example of what you are trying to achieve ? The error you posted has nothing to do with the code (SynchronizatorElement does not appear in the posted code). To get an answer, a minimal self contained way to reproduce your issue will helpTitian Cernicova-Dragomir
yes sorry, I changed the naming to make it more clear and forgot to change on the error :=> MyClassTwoBobby

3 Answers

1
votes

While mixins are a really great concept in general, I feel they don't really address the issue of extending two classes in a single one. Rather they enhance one class with a fixed set of extra functionality. Actually I don't think it's possible to extend two different, unrelated classes in a single one. But assuming we accept two minor accomodations we can get pretty close:

  1. Since the prototype chain is linear, instances of the extended class can only be instanceof one of its anchestors (let's say the first).
  2. Since TypeScript classes can only implement interfaces with statically known members (and no generic type variables) it's not possible to create a class extending two others in a generic way. We'll settle for a factory function instead.

Now, take a look at the following function:

type Constructable = new (...args: any[]) => any;

const mergeClasses = <S extends Constructable, T extends Constructable>(class1: S, class2: T) =>
    <Si extends InstanceType<S> = InstanceType<S>, Ti extends InstanceType<T> = InstanceType<T>>
    (args1: ConstructorParameters<S>, args2: ConstructorParameters<T>): Si & Ti => {
        const obj1 = new class1(...args1);
        const obj2 = new class2(...args2);
        for (const p in obj2) {
            obj1[p] = obj2[p];
        }
        return obj1 as Si & Ti;
    };

Calling it will return a factory function that constructs object instances implementing both classes originally passed to mergeClasses. For example to extend two classes

class MyClassOne {
    constructor(param1: number, param2: number) { }
}

class MyClassTwo<F, O> {
    constructor(param3: number, param4: number) { }
}

you'd just write the following (omit the type parameters if they're not generic):

const instanceFactory = mergeClasses(MyClassOne, MyClassTwo);
const instance = instanceFactory<MyClassOne, MyClassTwo<number, string>>([1, 2], [3, 4]);

You can also check out the sample code here.

1
votes

This is a simple example for extending a generic class and same way you can do multiple extends with generic type and even how you can initialize or create instance:

class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag;  // typechecks!
createInstance(Bee).keeper.hasMask;   // typechecks!
1
votes

You need to use generics to make sure the type of the original class is forwarded to the output of the function. This pattern is outlined here.

This code will work as expected keeping all type parameters in the output

const addMyClassOneInheritance = <T extends new(...args: any[]) => any>(MyClassOne: T)=> {
    return class extends MyClassOne { };
}

class MyClassTwo<F extends string, O extends number>{
    constructor(param3: F, param4: O) { }
}
const MyFinalClass = addMyClassOneInheritance(MyClassTwo);

export class SynchMapComponent extends MyFinalClass<string, number> {
}


new SynchMapComponent("", 1)