2

ISSUE:

I am attempting to return a class declaration from a higher order class method that extends a class from a class map {"SOME_CLASS" : SomeClass} thats key is a parameter of the higher order class method. However Typescript is throwing this error...
NOTE: I am not using any external libraries.

ERROR:

Cannot use 'new' with an expression whose type lacks a call or construct signature.

ATTEMPTS:

I have attempted to cast the Class Type as "Newable" however I lose my type binding for the class that is being extended.

SNIPPET

/// Contrived Example
interface BaseConfig {
  options: {
    profileHref: string
  }
}
abstract class Base { /*< BaseClass Implementations >*/
}
/// Example Class 1
class ProfileIcon extends Base {
  constructor(config: {
    options: {
      profileHref: string
    }
  }) {
    super(config);
  }
}
/// Example Class 2
class ScriptBlock extends Base {
  constructor(config: {
    options: {
      src: string
    }
  }) {
    super(config);
  }
}
}

class Manager {
  protected STORE: RootStore;
  constructor() {}
  public dispatchNewElement(elementType: keyof typeof ELEMENT_MANIFEST) {
    const storeShard = this.STORE.shard();
    const elementClass = ELEMENT_MANIFEST[elementType];


    /*
    //// ERROR: type 'typeof ProfileIcon | typeof ScriptBlock' is not a constructor function type.
    /// NOTE: 
    >> const T = new elementClass(...args)
    >> throws - Cannot use 'new' with an expression whose type lacks a call or construct signature.
    ////
    ////
    */

    return class extends /*err*/ elementClass /*endErr*/ {
      protected STORE_SHARD: typeof RootStore;
      constructor(elementConfig: { < unique fields to class implementation >
      }) {
        super(elementConfig);
        this.STORE_SHARD = storeShard;
      }
    }
  }

  /// Element Class Dictionary
  const ELEMENT_MANIFEST = {
    "PROFILE_ICON": ProfileIcon,
    "SCRIPT_BLOCK": ScriptBlock
  }

Please forgive any mis-formatting, this is maybe my second post on stack overflow. Cheers!

UPDATE from Comments

example of class returning class extending another class

class Master {
    public STATE: any;
    constructor() {
        this.STATE = { name: "foo" };
    }
    public dispatchNewClass(classType: string) {
        const myRefImage = Img;
        ////
        /* Works as a refVariable however..
        if i declare like...
            const myRefImage: Icon | Img
        I will get  
        >> Type 'typeof Img' is not assignable to type 'Icon | Img'.  
        >> Type 'typeof Img' is not assignable to type 'Img'.  
        >>Property 'TagName' is missing in type 'typeof Img'.
        */
        ///
        const asObject {}
        const ROOT = this.STATE;
        return class Slave extends myRefImage {
            protected ROOT: typeof ROOT;
            constructor(tagName: string) {
                super(tagName as "img")
                this.ROOT = ROOT;
            }
        }
    }
}
Erik Philips
  • 53,428
  • 11
  • 128
  • 150

1 Answers1

0

That would not work in TypeScript.

The type system in TypeScript only exists at compile time.

When the code is compiled, it is pure JavaScript.

You cannot define a new class and/or extends it from another class that is known only at runtime.

unional
  • 14,651
  • 5
  • 32
  • 56
  • Thank you for your input, however I do not see that to be 100% the case, I am able to do it when I extend the class name directly or from a reference variable. However I can not dynamically set the class it is extending dynamically. Is there potentially a type generic that would be fitting for this? Cheers! – SuspiciousCorgi Dec 30 '17 at 00:06
  • Can you post an example about what you mean by "extend the class name directly or from a reference variable"? – unional Dec 30 '17 at 00:56
  • I have updated the post to include this example. It seems like the issue is in the Union Type formed by `const extendClass : ClassA | ClassB = ClassA;` In theory I could just do a large switch / if else that returns each possible instance based on the passed "className" but I would only consider that if there are no other possibilities. Cheers – SuspiciousCorgi Dec 30 '17 at 01:32
  • In your example, it works because `myRefImage` can be analyzed statically to be the `Img` class. That's why it works. In your original question, it is determined dynamically so that is not possible. – unional Dec 30 '17 at 01:35
  • Ah, that makes sense. Is there a way to fool the type system to get around this? I'm thinking I will make a global ClassFactory function that includes functions to return each type of new extended class (that way I can use the static className) -- Unless there is an apparent better option. Thanks again! – SuspiciousCorgi Dec 30 '17 at 01:55
  • When you have a factory method, the end result can only be determined at runtime. So it is not possible. – unional Dec 30 '17 at 02:06
  • I would suggest to not use OO for this purpose. It would cause confusion to yourself down the road anyway. Use containment over inheritance. – unional Dec 30 '17 at 02:07