TypeScript: By providing a variable CLASS as an argument to a function, automatically determine the return type to be an object of the specified class without any additional information

I am looking to create a function that accepts actual class objects themselves as arguments (within an object containing multiple arguments), with the return type of the function being inferred to be an instance of the class provided as the argument.

In this scenario, we are dealing with an ORM where classes serve as models representing SQL tables. There will be several database query functions (separate from the classes) that will require arguments specifying the table to select data from.

Let's illustrate this concept with some code...

export class DogModelClass {
    id:number;
    name:string;
    woofCount:number;
}

export class CatModelClass {
    id:number;
    name:string;
    meowCount:number;
}

interface ArgumentsInterface {
    theActualClass; // <-- referring to the actual CLASS itself, not an instance
}

function getModelFromDatabase(args:ArgumentsInterface) {
    // code here retrieves a row for "dog" or "cat" from the DB and returns an INSTANCE of the specified args.theActualClass class
}

// Typescript should infer dogInstance as an INSTANCE of DogModelClass (based solely on the function arguments)...
const dogInstance = getModelFromDatabase({theActualClass:DogModelClass});

// Similarly, typescript should infer catInstance as an INSTANCE of CatModelClass (from the function arguments)...
const catInstance = getModelFromDatabase({theActualClass:CatModelClass});

I understand that I could use generics within the function like so:

function getModelFromDatabaseWithGeneric<ModelGeneric>(args:ArgumentsInterface):ModelGeneric {
    return <ModelGeneric>{};
}
const dogInstance2 = getModelFromDatabaseWithGeneric<DogModelClass>({theActualClass:DogModelClass});

However, I prefer not to specify the generic type every time I call the function, since the model class needs to be included in the arguments already for other purposes (such as determining the table to SELECT from). Having to repeat the class name each time I call querying functions seems redundant.

How can I achieve this without explicitly setting the generic type with each function call?

Any suggestions for more precise terminology in relation to passing around "the actual class object - not an instance" in JavaScript would also be appreciated.

Answer №1

One method to achieve this is by restricting the generic type to a class constructor and then returning its InstanceType:

function getDataModel<T extends { new(...args: any[]): any }>(input: { actualClass: T }): InstanceType<T> { /* ... */ }

Alternatively, you can ensure that the argument is always of constructor type:

function getDataModel<T>(input: { actualClass: {new (...args: any[]): T} }): T { /* ... */ }

Both approaches achieve the same result:

// Inferred as an INSTANCE of DogDataModel 
const dogInstance = getDataModel({actualClass: DogDataModel});

// Inferred as an INSTANCE of CatDataModel
const catInstance = getDataModel({actualClass: CatDataModel});

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

Generate a versatile Union type featuring a mapped property

I am currently working with different types of data enum DataTypes { Email = 'email', Checkbox = 'checkbox', } type DataTypeValues = { [DataTypes.Email]: string; [DataTypes.Checkbox]: boolean; }; type Type1<T extends DataTy ...

When organizing Node.js express routes in separate files, the Express object seamlessly transforms into a Router object for efficient routing

I am currently working on a Node.js application with Express. I organize my routes using tsoa, but when I introduce swagger-ui-express to the project, an error occurs Error: TypeError: Router.use() requires a middleware function but got undefined Here is ...

Creating a Bottom Sliding Menu in Ionic 2

Hey there! I'm working on something that might not fit the typical definition of a sliding menu. It's not for navigation purposes, but rather to house some data. My idea for this actually comes from Apple Maps: https://i.stack.imgur.com/hL1RU.jp ...

Having trouble accessing a JSON object with Typescript in an Angular 2 project

Something strange is happening with my code. I am working with a JSON object: {"login":"admin","name":"Admin"} And this is the relevant part of my code: private _userData: User; ... private getUserData() { this._userInfoService.getUserInfo() ...

Does Apollo Federation provide support for a code-first development approach?

I have declarations using 'code-first' approach in my project, but now I want to utilize them as microservices. How can I separate my 'typeDefs' and 'resolvers' following Apollo's 'schema-first' methodology? Is ...

Error: Unable to generate MD5 hash for the file located at 'C:....gradle-bintray-plugin-1.7.3.jar' in Ionic framework

When attempting to use the command ionic cordova run android, an error occurred that prevented the successful execution: The process failed due to inability to create an MD5 hash for a specific file in the specified directory. This issue arose despite suc ...

Can NODE_PATH be configured in Typescript?

Before, I worked on my React app with ES6 and used NODE_PATH='src' to import files starting from the src folder. However, since switching to Typescript, I've realized that NODE_PATH is not supported. After some investigation, I discovered th ...

Customizing default attribute prop types of HTML input element in Typescript React

I am currently working on creating a customized "Input" component where I want to extend the default HTML input attributes (props). My goal is to override the default 'size' attribute by utilizing Typescript's Omit within my own interface d ...

The input '{ data: InvitedUser[]; "": any; }' does not match the expected type 'Element'

I'm currently facing a typescript dilemma that requires some assistance. In my project, I have a parent component that passes an array of results to a child component for mapping and displaying the information. Parent Component: import { Table } fr ...

Using Fixed Patterns and Combining Types in an Interface

Presently, I am working with this interface: export interface User{ name: string birthday: number | Timestamp ... } When strictTemplates:false is enabled, I have no issue using this interface for server data retrieval with the birthday parameter in ...

Unique: "Unique One-Step Deviation in Date Comparison"

A Little Background Information: I am working with data points that span from the current day to 5 days ahead, in 3-hour intervals (such as 10pm, 1am, 4am, 7am...). My goal is to organize these data points into arrays sorted by date, with each array repre ...

Eliminating the nested API call within a loop

After making an API call to retrieve a set of data such as a list of users, I noticed that I am implementing a for loop and within it, I am making another API call to obtain each user's profile details based on their ID. I understand that this approac ...

Step-by-step guide on importing CSS into TypeScript

I have a global CSS file where I've defined all the colors. I attempted to import that value into TypeScript but it didn't work. This is my latest attempt: get sideWindowStyle(): any { switch (this.windowStyle) { case 'classicStyl ...

How can I pass a service method as a parameter in an Angular 2 component?

Within the component: myFunction(): void { this.myOtherFunctoin(this._myService.serviceMethod); } private myOtherFunction(func : Function){ func(); } Regarding service calls: serviceMethod(){ this.somethingMethod(); // "this" is coming as ...

Transforming Javascript into Typescript with node modules in Visual Studio 2015

After developing a JavaScript web app using npm and webpack, I successfully converted all the .js files to .ts using the PowerShell command found here. Now, I am looking to transition to a VS2015 TypeScript project but struggling to find guidance on how ...

What is the best way to upload a file using a relative path in Playwright with TypeScript?

Trying to figure out how to upload a file in a TypeScript test using Playwright. const fileWithPath = './abc.jpg'; const [fileChooser] = await Promise.all([ page.waitForEvent('filechooser'), page.getByRole('button' ...

The Gatsby + Typescript project is reporting that the module with the name "*.module.scss" does not have any exported members

I've recently gone through Gatsby's demo project in their documentation (which is long overdue for an update). I've carefully followed the instructions provided here: I've included an index.d.ts file in the /src directory of my project ...

How can an array of file paths be transformed into a tree structure?

I am looking to transform a list of file and folder paths into a tree object structure (an array of objects where the children points to the array itself): type TreeItem<T> = { title: T key: T type: 'tree' | 'blob' childr ...

The `role` property is not recognized in the type `User | AdapterUser` within NextAuth

In my attempt to develop a NextJS application integrated with NextAuth, I am facing an error in my [...nextauth].ts file while setting up the callbacks: Type error: Property 'role' does not exist on type 'User | AdapterUser'. Property ...

Utilizing Ionic with every Firebase observable

Can someone help me with looping through each object? I am trying to monitor changes in Firebase, and while it detects if there is a push from Firebase, I am facing issues displaying it in the HTML. Where am I going wrong? Thank you! ping-list.ts p ...