0
interface IBase {
  baseProp?: string;
}

class Base implements IBase {
  baseProp: string = "baseProp";
}

type AnyClass<T> = new () => T;

interface IClass1 extends IBase {
  class1Prop?: string;
}

const Class1Mixin = (MixedClasses: AnyClass<any>): AnyClass<IClass1> =>
  class extends MixedClasses implements IClass1 {
    class1Prop: string = "class1Prop";
  };

interface IClass2 extends IBase {
  class2Prop?: string;
}

const Class2Mixin = (MixedClasses: AnyClass<any>): AnyClass<IClass2> =>
  class extends MixedClasses implements IClass2 {
    class2Prop: string = "class2Prop";
  };

// Mix classes
const mix = <T>(...extensions: Array<(anyClass: any) => AnyClass<T>>) => {
  let Composition = Base as AnyClass<IBase>;

  for (const ext of extensions) {
    Composition = ext(Composition);
  }
  return Composition as AnyClass<T>;
};

// Here can be any number of classes
const Mixed = mix(Class1Mixin, Class2Mixin);
const mixedInit = new Mixed();
// Now mixedInit is IBase & ICLass1
// and I need that it will be IBase & ICLass1 & ICLass2

console.log(mixedInit.baseProp, mixedInit.class1Prop, mixedInit.class2Prop);

I have a function that mixes any number of classes all together with Base class. But I couldn't mix interfaces of theses classes. Please tell me how can I improve mix function types to do that?

EEM
  • 3
  • 1
  • The closest thing would be [generics](https://www.typescriptlang.org/docs/handbook/generics.html), but `interfaces` and `class`es cannot be mixed using functions as `interface`s are purely types with no values attached to them (contrary to `enums` and `class`es) – Elias Schablowski Aug 11 '20 at 07:36

1 Answers1

0

Following code is solution.The essence of this solution is transform union type to intersection type.You can read the answer about the question of transform union type to intersection type

interface IBase {
  baseProp?: string;
}

class Base implements IBase {
  baseProp: string = "baseProp";
}

type AnyClass<T> = new () => T;

interface IClass1 extends IBase {
  class1Prop?: string;
}

const Class1Mixin = (MixedClasses: AnyClass<any>): AnyClass<IClass1> =>
  class extends MixedClasses implements IClass1 {
    class1Prop: string = "class1Prop";
  };

interface IClass2 extends IBase {
  class2Prop?: string;
}

const Class2Mixin = (MixedClasses: AnyClass<any>): AnyClass<IClass2> =>
  class extends MixedClasses implements IClass2 {
    class2Prop: string = "class2Prop";
  };


type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

// Modified Mix classes
const mix = <T extends Array<(MixedClasses: AnyClass<any>) => AnyClass<any>>>(...extensions: T): AnyClass<UnionToIntersection<InstanceType<ReturnType<T[number]>>>> => {
  let Composition = Base as AnyClass<IBase>;

  for (const ext of extensions) {
    Composition = ext(Composition);
  }
  return Composition as any;
};

const Mixed = mix(Class1Mixin, Class2Mixin);
const mixedInit = new Mixed();
// Now mixedInit is ICLass1 & ICLass2

console.log(mixedInit.baseProp, mixedInit.class1Prop, mixedInit.class2Prop);
cole
  • 48
  • 4
  • You are best of the best 8) Thanks. Maybe you can help me with one thing: `type AnyClass = new (config?: T['config']) => T;` How could be config key extracted from T without error? – EEM Aug 11 '20 at 09:24
  • @EEM Try to declare the essential of T type.Example: `type AnyClass = new (config?: T['config']) => T` – cole Aug 11 '20 at 09:38