skip to Main Content

I’ve got an instance where I want to create X number of Classes that all need a constructor to assign a logging service, and then all have a method create which will handle the data object differently depending on the class.

I then want to pass this class into a function dynamically so that it can be instantiated and the create method called.

If I try the code below I receive the error TypeError: ContentCreator is not a constructor, and I can’t find anywhere in the TypeScript documentation to hint at how to pass a class of type extended into a function, and then how to call the constructor to create a new instance.

import { ApplicationLogger } from "application-logger";

interface Logger {}

interface Message {
  data: any;
}

class ContentCreator {
  logger;

  constructor(logger: Logger) {
    this.logger = logger;
  }

  public async create(body: Message): Promise<string[]> {
    return new Promise(() => []);
  }
}

class PublicationContentCreator extends ContentCreator {
  constructor(logger: Logger) {
    super(logger);
  }

  public override async create(body: Message): Promise<string[]> {
    console.log("Publication: ", body);

    return new Promise(() => []);
  }
}

class EventContentCreator extends ContentCreator {
  constructor(logger: Logger) {
    super(logger);
  }

  public override async create(body: Message): Promise<string[]> {
    console.log("Event: ", body);

    return new Promise(() => []);
  }
}

function connectAndGetMessage(connectionString: string) {
  // ...
}

async function readFromQueue(connectionString: string, ContentCreator: any) {
  const logger = new ApplicationLogger();
  const creator = new ContentCreator(logger);
  const queueData = connectAndGetMessage(connectionString);
  creator.create(queueData);
}

(async () => {
  await readFromQueue("connection-string", PublicationContentCreator);
})();

2

Answers


  1. Chosen as BEST ANSWER

    So after a lot of digging about (asking Chat-GPT a lot of different combinations) it looks like the key is to type the function parameter as a Constructable Class, which then allows any Class that extends ContentCreator to be passed in and instantiated.

    async function readFromQueue(connectionString: string, CreatorConstructor: new (
      logger: Logger
    ) => ContentCreator) {}
    

    The whole code then looks like this:

    import { ApplicationLogger } from "application-logger";
    
    interface Logger {}
    
    interface Message {
      data: any;
    }
    
    abstract class ContentCreator {
      logger: Logger;
    
      constructor(logger: Logger) {
        this.logger = logger;
      }
    
      public abstract create(body: Message): Promise<string[]>;
    }
    
    class PublicationContentCreator extends ContentCreator {
      constructor(logger: Logger) {
        super(logger);
      }
    
      public async create(body: Message): Promise<string[]> {
        console.log("Publication: ", body);
    
        return [];
      }
    }
    
    class EventContentCreator extends ContentCreator {
      constructor(logger: Logger) {
        super(logger);
      }
    
      public async create(body: Message): Promise<string[]> {
        console.log("Event: ", body);
    
        return [];
      }
    }
    
    function connectAndGetMessage(connectionString: string): Message {
      return {} as Message;
    }
    
    async function readFromQueue(connectionString: string, CreatorConstructor: new (
      logger: Logger
    ) => ContentCreator) {
      const logger = new ApplicationLogger();
      const creator = new CreatorConstructor(logger);
      const queueData = connectAndGetMessage(connectionString);
      creator.create(queueData);
    }
    
    (async () => {
      await readFromQueue("connection-string", PublicationContentCreator);
    })();
    

  2. The ContentCreator class is an abstract class, so it shouldn’t be instantiated directly. This can be fixed by adding the abstract modifier to the ContentCreator class declaration.

    Also, the ContentCreator argument in the readFromQueue() function is not used and can be removed. Instead, the function should be given a concrete class that extends the abstract ContentCreator class. For example, we can pass the PublicationContentCreator class as an argument to the function.

    The corrected code would look like this:

    import { ApplicationLogger } from "application-logger";
    
    interface Logger {}
    
    interface Message {
      data: any;
    }
    
    abstract class ContentCreator {
      logger: Logger;
    
      constructor(logger: Logger) {
        this.logger = logger;
      }
    
      public abstract async create(body: Message): Promise<string[]>;
    }
    
    class PublicationContentCreator extends ContentCreator {
      constructor(logger: Logger) {
        super(logger);
      }
    
      public async create(body: Message): Promise<string[]> {
        console.log("Publication: ", body);
    
        return [];
      }
    }
    
    class EventContentCreator extends ContentCreator {
      constructor(logger: Logger) {
        super(logger);
      }
    
      public async create(body: Message): Promise<string[]> {
        console.log("Event: ", body);
    
        return [];
      }
    }
    
    function connectAndGetMessage(connectionString: string) {
      // ...
    }
    
    async function readFromQueue(connectionString: string, CreatorClass: typeof ContentCreator) {
      const logger = new ApplicationLogger();
      const creator = new CreatorClass(logger);
      const queueData = connectAndGetMessage(connectionString);
      creator.create(queueData);
    }
    
    (async () => {
      await readFromQueue("connection-string", PublicationContentCreator);
    })();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search