Guide on creating a detailed list of categories mapped to specific classes that all adhere to a common generic standard

Most TypeScript factory patterns I've encountered rely on a named mapping between a name and the Class type.

A basic implementation example:

const myMap = {
 classOne: ExampleClass,
 classTwo: AnotherClass
}

(k: string) => { return new myMap[k] }

I wanted to elevate this mapping concept - as my classes follow the structure of ExampleClass implementing iGenericInterface<T>

However, I'm struggling to specify that Record Value types should be restricted to those implementing the iGenericInterface<T>. The problematic area in the following example is marked with ***

export interface iGenericInterface<K> {
  process(input: K): void
}

class ExampleClass implements iGenericInterface<string> {
  process(input: string): void {
    console.log(input);
  }
}

class AnotherClass implements iGenericInterface<number> {
  process(input: number): void {
    console.log(input);
  }
}

class UnsupportedClass {
  otherMethod(input: boolean): void {
    console.log(input);
  }
}

enum instantiableClassesEnum { class1, class2, class3 };
type enumValues = keyof typeof instantiableClassesEnum;
type logicStore<T> = Record<enumValues, ***iGenericInterface<T>***>

const classMap: logicStore<any> = {
    class1: ExampleClass,
    class2: AnotherClass,
    class3: UnsupportedClass,
};

I would anticipate an error for the UnsupportedClass, but desire acceptance by the compiler for the others.

If I change ExampleClass to an instance like class1: new ExampleClass, the compiler is content. However, I then face uncertainty regarding how to utilize the mapping in the factory method - return new myMap[k]

EDIT - (@Adriaan This is NOT an answer, just a clarification of the end goal) Ability to generate classes based on Enumeration input - utilizing the mapped structure above.

class LogicFactory {
  static createLogic<T>(logicClass: enumValues): 
  iGenericInterface<T> {
  return new classMap[logicClass]();
}

}

Answer №1

To utilize the Constructor<T> type, follow this code snippet:

type _builtin = InstanceType<any> // T extends abstract new (...args: any) => infer R ? R : any
type Constructor<T> = new (...args: any) => T

enum instantiableClassesEnum { class1, class2, class3 };
type enumValues = keyof typeof instantiableClassesEnum;
type logicStore<T> = Record<enumValues, Constructor<iGenericInterface<T>>>

const classMap: logicStore<any> = {
    class1: ExampleClass,
    class2: AnotherClass,
    class3: UnsupportedClass, // Property 'process' is missing in type 'UnsupportedClass'
};

Answer №2

After reviewing @Dimava's solution on how to establish the Enum - Constructor correlation, the desired outcome has been successfully attained:

Below is an elaborate explanation of the solution provided by @jcalz

type Constructor<T> = new (...args: any) => T
enum instantiableClassesEnum { class1, class2, class3 };
type enumValues = keyof typeof instantiableClassesEnum;
type logicStore<T> = Record<enumValues, 
Constructor<iGenericInterface<T>>>

const classMap: logicStore<any> = {
    class1: ExampleClass,
    class2: AnotherClass,
    class3: UnsupportedClass,
};


class LogicFactory {
  static createLogic<T>(logicClass: enumValues): iGenericInterface<T> {
    return new classMap[logicClass]();
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Angular 8 combined with Mmenu light JS

Looking for guidance on integrating the Mmenu light JS plugin into an Angular 8 project. Wondering where to incorporate the 'mmenu-light.js' code. Any insights or advice would be greatly appreciated. Thank you! ...

Utilizing TypeScript union types in React: A step-by-step guide

I'm currently working on applying types to ReactJS props using an interface that includes a union type. In the example below, you can see that the tags type is a union type. export interface TagInterface { id: number; name: string; } export inter ...

Cannot assign Angular 4 RequestOptions object to post method parameter

I'm having trouble with these codes. Initially, I created a header using the code block below: headers.append("Authorization", btoa(username + ":" + password)); var requestOptions = new RequestOptions({ headers: headers }); However, when I tried to ...

Prisma : what is the best way to retrieve all elements that correspond to a list of IDs?

I'm currently implementing Prisma with NextJs. Within my API, I am sending a list of numbers that represent the ID's of objects in the database. For example, if I receive the list [1, 2, 12], I want to retrieve the objects where the ID is eithe ...

I'm encountering an issue trying to apply array filtering with checkboxes using React hooks and TypeScript

Help needed! I'm facing an issue while trying to filter an array based on gender using checkboxes. The functionality works fine for the male checkbox but seems to fail when clicking on the female checkbox. Below is the code snippet from my App.tsx fil ...

Dynamic Route Matching in NextJS Middleware

Currently, I am in the process of developing a website that incorporates subdomains. Each subdomain is linked to a file-based page using middleware. Take a look at how the subdomains are being mapped to specific pages: app.com corresponds to /home app.com ...

Adding an additional element to an object - crossroads of combining types versus sequential examination

Here's a query for you: AppendToObject. When I first tackled this question, my initial instinct was to utilize type intersection like so: type AppendToObject<T, U extends PropertyKey, V> = T & {[P in U]: V} Unfortunately, this solution did ...

Is the regex returning the correct result?

I need to discuss the date field with a format of YYYYMMDD, as shown below: zod.string().max(8).regex(new RegExp('^(19[0-9][0-9]|20[0-9][0-9]|[0-1][0-9]{3})(1[0-2]|0[1-9])(3[01]|[0-2][1-9]|[12]0)$')); The value provided is 20001915. The definit ...

Error: The Turborepo package restricts the use of import statements outside of modules

I created a typescript "test" package in turborepo, which imports and exports typescript functions. Due to being in turborepo, it gets copied to node_modules/test. When attempting to run import {func} from "test", an error is thrown: SyntaxError: Cannot ...

TypeError thrown by Basic TypeScript Class

I'm encountering an issue where TypeScript is throwing a TypeError when trying to use the class Literal from file Classes.tsx in file App.tsx, even though they are in the same file. Strangely, everything works fine on typescriptlang.org/play. // Class ...

The error message "Cannot bind to 'ngForOf' because it is not recognized as a valid property of the element."

After utilizing NGFor for quite some time, I encountered an unexpected issue in a new application where I received the error message: Can't bind to 'ngForOf' since it isn't a known property of 'div'.' I found it strang ...

Incorrect tsx date interpretation when dealing with years such as 0022

I am facing an issue with dates in tsx. The problem lies in the fact that when I set a date like 30/11/0022, it interprets the date as 30/11/1922, which is incorrect. Here is the input element I have in tsx: <FormikField name="Birthdate" disa ...

Unable to determine all parameters for Modal: (?, ?, ?)

import { Component, Inject } from '@angular/core'; import { NavController, Modal } from 'ionic-angular'; import { PopupPage } from '../../components/modal/modal.page'; @Component({ templateUrl: 'build/pages/spot/spot. ...

"What is the best way to determine the data type of an object retrieved from an API in TypeScript

Hey there, I'm currently developing a web application using Angular 2 and I'm focusing on implementing an exception handling mechanism. To achieve this, I've created a model that mirrors the object structure I will receive from the server (E ...

What is causing the failure of the state to be inherited by the child component in this scenario (TypeScript/React/SPFX)?

For this scenario, I have a Parent class component called Dibf and a Child class component named Header. While I can successfully pass props from the Parent to the child, I am encountering difficulties when trying to pass state down by implementing the fo ...

The function does not throw a compiler error when a parameter is missing

Currently utilizing TSC Version 2.4.2 Please take note of the following interface: interface CallbackWithNameParameter { cb: (name: string) => void } This code snippet: const aCallback: CallbackWithNameParameter = { cb: () => {} }; Manages t ...

When using nodejs with sqlite3, the first callback parameter returns the class instance. How can this be resolved in order to prevent any issues?

Exploring a TypeScript class: class Log { public id: number; public text: string; construct(text: string){ this.text = text; } save(){ db.run( `insert into logs(text) values (?) `, this.text, ...

Creating a new element in the DOM when a button is clicked using Angular 2+

Greetings! I am currently in the process of learning how to manipulate the DOM using Angular 2+ and my goal is to incorporate an Input field with type email when the click event is activated. Despite conducting some research via Google, I have been unable ...

Steps to access a Request object within a Controller

I am currently working with Express and Typescript, utilizing Controllers for managing requests. In an attempt to create a BaseController that includes the Request and Response objects for each request, I wrote the following code snippet. However, it see ...

Implementing TypeScript with styled components using the 'as' prop

I am in the process of developing a design system, and I have created a Button component using React and styled-components. To ensure consistency, I want all my Link components to match the style and receive the same props as the Button. I am leveraging t ...