using any class as a function parameter in TypeScript

Looking for advice: I am new to TypeScript and have classes defined like the ones below:

export declare class SampleOne extends Setting {
    getValue(): Promise<boolean>;
    setValue(value: boolean): Promise<void>;
}

And

export declare class SampleTwo extends Setting {
    getValue(): Promise<boolean>;
    setValue(value: boolean): Promise<void>;
}

Now I want a helper function where I can pass either SampleOne or SampleTwo as follows:

async function getObj(title: string, categories: string, cname: Setting) {

    let obj = await et.findSetting(title,categories) as cname;// I want to be able to pass either **SammpleOne** or **SampleTwo** class.
    return obj;
}

The function call would be:

getObj(title, categories, SampleOne)

getObj(title, categories, SampleTwo)

I am struggling to create this helper function. What should it look like in TypeScript?

Thank you in advance.

Answer №1

To create a generic version of the function getObj, you can specify the type Setting and use the last argument as a constructor for type T:

type Class<T> = new (...args: any) => T;

async function getGenericObj<T extends Setting>(
    title: string, categories: string, klass: Class<T>): Promise<T> 
{
    return .... as  T
}

Answer №2

I think the solution lies in applying the constraint T extends typeof Setting to the generic:

class Setting {
    getValue(): Promise<boolean> { return Promise.resolve(true) };
    setValue(value: boolean): Promise<void> { return Promise.resolve() };
}


class SampleOne extends Setting {
    getValue(): Promise<boolean> { return Promise.resolve(true) };
    setValue(value: boolean): Promise<void> { return Promise.resolve() };
}

class SampleTwo extends Setting {
    getValue(): Promise<boolean> { return Promise.resolve(true) };
    setValue(value: boolean): Promise<void> { return Promise.resolve() };
}

class DontAllowed { }

type AllowedClasses = typeof SampleOne | typeof SampleTwo

class SampleThree { // does not extends Setting
    getValue(): Promise<boolean> { return Promise.resolve(true) };
    setValue(value: boolean): Promise<void> { return Promise.resolve() };
}

function getObj<T extends typeof Setting>(cname: T) {
    return cname
}

const result = getObj(SampleOne) // ok
const result2 = getObj(SampleTwo) // ok
const result3 = getObj(DontAllowed) // expected error

In the example above, it is assumed that every class extending Setting is allowed. If you only want to allow two specific classes, you can modify it like this:


type AllowedClasses = typeof SampleOne | typeof SampleTwo

function getObj<T extends AllowedClasses>(cname: T) {
    return cname
}

Playground

I personally believe that using type assertion (as operator) in such scenarios may not be necessary.

Answer №3

I personally prefer the Convenience generic design pattern

async function getObj<T extends Setting>(title: string, categories: string): Promise<T> {
    let obj = await et.findSetting(title,categories) as T;
    return obj;
}

Alternatively, you could simplify it by removing the unnecessary async keyword:

function getObj<T extends Setting>(title: string, categories: string): Promise<T> {
    return et.findSetting(title,categories) as Promise<T>;
}

Example of how to use this function:

const s1 = getObj<SampleOne>('a', 'b');

Advantages:

  • Can be utilized if Setting is modeled as a class
  • Can also be used if Setting is modeled as an interface

Disadvantages:

  • No runtime check included (which may or may not be an issue)

If runtime checking is needed and settings are represented as classes, you can consider using the Type approach inspired by Angular:

interface Type<T> extends Function { new (...args: any[]): T; }

async function getObj<T extends Setting>(
    title: string, categories: string, klass: Type<T>): Promise<T> 
{
   let obj = await et.findSetting(title,categories);
   if (klass.prototype.isPrototypeOf(obj)) {
    return obj as T; 
   } else {
     throw new Error(`Expected ${klass.name} but got ${obj.constructor.name}`)
   }
   
}

const s1 = getObj('a', 'b', SampleOne);
s1.then(x => console.log(x))
  .catch(e => console.log(e));

Answer №4

Utilize generics for enhanced functionality.

async function fetchItem<T>(name: string, category: string) {

    let item = await retrieveData(name, category) as T;
    return item;
}

You can then invoke the function with the specific type you want:

fetchItem<SampleOne>('example', 'typeA');
fetchItem<SampleTwo>('sample', 'typeB');

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

Storing and Retrieving Mongoose Data Using Nested Schemas, References, and Promise Functions

I have a question regarding saving and retrieving documents with nested schema references in Mongoose. When I try to save a document with nested schema refs, and then retrieve it later, the required nested field is not included unless I populate it in the ...

Iterate over a series of button elements within a loop in order to dynamically control corresponding input fields

Having trouble looping through an array with JavaScript/jQuery to display data and remove contents with button clicks. Original Code in Question HTML <textarea rows="4" cols="40" id="sds">Apple Banana Grape Orange</textarea> <input type=" ...

Disabling form submission when pressing the enter key

Is there a way to prevent a submit action from occurring when the enter key is pressed within an ASP:TextBox element that triggers an asyncpostback upon text change? Instead, I would like it to click on another button. The Javascript function I am using wo ...

What is the best way to transfer data between functions prior to serializing and submitting the form?

Here are two functions I am working with: $("#form_pdetail").on("click", "#register_button", function() { var detail_add = $("#form_pdetail").serialize(); var request = $.ajax({ type: 'POST', url: "{{ path('product_d ...

Send information to the next route using Vue

Within my Vue frontend, there is a method called `moveToOrder` which asynchronously communicates with the backend to process a move from the cart collection to the orders collection: methods:{ async moveToOrder() { const res = await this.$axios.g ...

Exploring the power of Vue element manipulation

I'm diving into the world of web development and starting my journey with Vue on an online learning platform. Check out the code snippet below: <div id="app"> <form @submit.prevent="onSubmit"> <input v-model="userName"&g ...

React: Oops! Looks like there's an issue - this.props.update is not defined as

Hello everyone, I'm diving into the world of programming for the first time and I might ask some silly questions along the way. Currently, I'm working on integrating a date picker into a search application built with React. The user should be ab ...

Can TypeScript types be created using multiple comma-separated strings?

Is it feasible to define a custom type in TypeScript like Type LayoutType = "Left" | "Right" | "Top" | "Bottom" | "VCenter", that would combine values such as "Left,VCenter"? Or do I need to create a string literal for every possible combination? ...

Obtain a union type using the `keyof typeof` syntax

Is there a way to retrieve the union or enum type from a typeof type in TypeScript? For instance: const myConfs: { [k: string]: (myArg: { name: string }) => string } = { 'Hello': ({ name }) => `World from ${name}`, 'Goodbye': ...

javascript alter css property display to either visible or hidden

I am struggling with a CSS id that hides visibility and uses display: none. The issue arises when I want the element to be visible upon clicking a button, but removing the display:none property is causing design problems due to it being an invisible elemen ...

Issue with SignalR client functionality following update to .NET Core 3.1版本

Upon updating our server-side code to asp.net core 3.1, we encountered an issue with the javascript client for signalr (@microsoft/signalr 3.1.0). The errors we are facing are: https://i.sstatic.net/ITZyK.png Here is the code snippet for the hub initial ...

Angular JWT token validation error

I am currently experiencing an issue with the Angular JWT token. It works perfectly fine on my API when tested using Postman, but for some reason it is not working when called from Angular. Here are the request details: https://i.sstatic.net/L6Tsr.png I c ...

I would like a div element to slide up from the bottom of the page

Could someone please assist me in creating a popup div that appears from bottom to top when a specific button is clicked? I would like the div to be fixed without affecting the overall page content. Any help would be greatly appreciated. Thank you! $( ...

What is the best way to ensure that a task is performed only once the DOM has finished loading following an AJAX request

<div id="mydiv"> ... <a id="my-ajax-link"> ... </a> ... ... <select id="my-selectmenu"> ... </select> ... </div> Upon clicking the 'my-ajax-link' link, it triggers an AJ ...

Button for PayPal Checkout

I am currently working on setting up a functional Paypal button for a personal e-commerce website. Everything runs smoothly in my sandbox testing environment, processing payments and returns without any issues. However, when I switch the client ID to the ...

Oops! The program encountered an issue on the production environment, but it's running smoothly

When I execute Webpack using the command node node_modules/webpack/bin/webpack. js --env. prod An error message is displayed below. However, when running in --env. dev mode, the command executes without any issues. Can't resolve './../$$_gen ...

What is the reason I am unable to upload personalized templates using <script> elements?

I have a dilemma where I need to swap out multiple templates from a library with my own custom ones without forking the original templates. When I include the templates in my index.html page like: <script type="text/ng-template" id="first-template"> ...

Confusion surrounding asynchronous functions in Node.js

When handling routes or endpoints with multiple operations, I often encounter scenarios where I need to perform additional actions. For instance, when deleting an item, it's necessary to also remove the related file from S3 along with deleting the col ...

iPhone App Freezes when trying to download a file through Phonegap

Encountering an issue while downloading a file with phonegap: if the internet connection is lost, the application hangs and crashes. The error message received is: * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: ...

Tips for locating the :before element's position with Javascript

I am currently working on an animation where I need to change the color of an element in the center once a small circle touches it. Despite trying to use the position() method, it does not work as intended because I am using a :before for animating the div ...