Retrieving properties of a universal function

I am facing a challenge in creating a class that accepts a factory function as one of its parameters in the constructor, which is then used to create instances of another class. The implementation below is written in JavaScript.

// item to create
class Item{}

function factory(someData){
 return new Item()
}

class Repository{
  constructor(factory){
     this.factory = factory
  }

  createItem(optionalData){
   return this.factory(optionalData)
 }
}

The objective now is for the Repository class to accept a generic item, and ensuring that the factory function creates that generic item.

My struggle lies in extracting the parameters from the factory property and using them within the createItem method. I want the createItem method to mirror the arguments of the factory function.


class Item {
  constructor(public data:{id:string,name:string}){
  }
}

// trying to type the factory function
export type ModelFactory<TModel> = (...args:any[]) => TModel


class Collection<TModel >{

  constructor(public factory:ModelFactory<TModel>){}

  // attempting to extract factory function parameters
  createItem(data?:Parameters<ConstructorParameters<typeof Collection>[0]>[0]){
    return this.factory(data)
  }
}

// implementation
const createItemFactory:ModelFactory<Item> = function (data:{id:string,name:string}){
  return new Item(data)
}

const coll = new Collection<Item>(createItemFactory)

// arguments passed to createItem() should be strongly typed
const itemInstance = coll.createItem(1)

Issues:

  • When calling coll.createItem(), the arguments are typed as any
  • The factory function could have a signature with no arguments.

I am finding it challenging to address these scenarios.

TS Playground

Answer №1

It would be beneficial to make Collection generic in both the return type of factory (referred to as TModel and hereafter as T) and also in the argument list using a rest tuple (referred to as A):

class Collection<A extends any[], T>{
  constructor(public factory: (...args: A) => T) { }
  createItem(...args: A) {
    return this.factory(...args)
  }
}

By calling this.factory() with spread syntax, it addresses potential issues with zero or one arguments and supports functions that take any number of arguments. Let's test it:

const coll = new Collection(createItemFactory)
coll.createItem(1); // error!
// Argument of type 'number' is not assignable to parameter of type '{ id: string; name: string; }'
const itemInstance = coll.createItem({ id: "a", name: "b" }) // okay

// no args
const c2 = new Collection(() => ({ a: 123 }));
console.log(c2.createItem().a.toFixed(2)); // 123.00

// more than one args
const c3 = new Collection((x: number, y: string) => ({ x, y }));
console.log(c3.createItem(1, "b")); // {x: 1, y: "b"}

It appears to work well.

Link to Playground for code testing

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

The IDE is showing an error, but Jest is able to run it flawlessly

I recently created a Jest unit test for a TypeScript function called checkEmail, which internally uses showAlert. The showAlert function in the utils.ts file looks like this: export const showAlert = (message: string) => { toast(message); }; In my ...

Determining the data type of a property within an interface using TypeScript

Is there a way to extract the type from an interface based on its property name in order to use it in a Record? I am struggling with the syntax needed to retrieve the type by property name. My goal is to make this process more future-proof so that if the i ...

Issue encountered when importing a font in TypeScript due to an error in the link tag's crossorigin

How do I troubleshoot a TypeScript error when importing a custom font, such as a Google font? <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> Below is the specific error message: Type 'boolean' is ...

Is there an issue with this return statement?

retrieve token state$.select(state => { retrieve user access_token !== ''}); This error message is what I encountered, [tslint] No Semicolon Present (semicolon) ...

Error: "Reflect.getMetadata function not found" encountered during execution of Jenkins job

My Jenkins job is responsible for running tests and building an image. However, I am encountering issues with the unit tests within the job. task runTests(type: NpmTask) { dependsOn(tasks['lintTS']) args = ['run', 'test&ap ...

Provide an immutable parameter to a function that will not cause any changes

Looking to develop a function named batchUsers, requiring a parameter of type readonly string in order to create a DataLoader. However, when calling the User.findBy function within my batchUsers function, it's causing issues due to conflicting paramet ...

Retrieve selected button from loop typescript

https://i.stack.imgur.com/DS9jQ.jpgI have an array of individuals that I am looping through. It's a bit difficult for me to explain, but I want something like this: <div *ngFor="let person of persons"> {{person.name}} {{person.surname}} <but ...

The TypeScript error "Issue with Type Assertion: 'This expression is not callable Type'...' has no call signatures" occurs when there is a missing semicolon

Here's a simplified version of our original code: const start: number = 10 const end: number = 20 (someElement as HTMLInputElement).setSelectionRange(start, end) We encountered an error with the 20, where a red squiggly line appeared indicating ...

Is it feasible to use a component in a recursively manner?

Following a two-hour search for a solution, I decided to reach out to experts as I suspected the answer might be simpler than expected. The project in question is an Angular7 one. In my goals component, I aim to include a "goal" with a button labeled "+". ...

What is the best way to expose the "nuxtServerInit" action for Nuxt.js when using dynamic modules exclusively?

According to this answer, the code snippet below is taken from the official documentation of vuex-module-decorators // @/store/index.ts import Vuex from 'vuex' const store = new Vuex.Store({ /* Ideally if all your modules are dynamic then ...

Error: The TypeScript aliases defined in tsconfig.json cannot be located

Having trouble finding the user-defined paths in tsconfig.json – TypeScript keeps throwing errors... Tried resetting the entire project, using default ts configs, double-checked all settings, and made sure everything was up-to-date. But still no luck. H ...

How can I conceal an element upon page load using Ionic framework?

index.component.ts initialize() { // Execute necessary functions after loading this.index.ready().then(() => { if(this.index.is('core')){ // this.menu.enable(false, 'mobileOnlyPages'); }else{ ...

Tips for creating a dynamic interface in TypeScript to eliminate repetitive code

Currently, I am utilizing typescript alongside the react-navigation library within my react-native project. Following the guidelines provided in the official documentation, I have been annotating my screens. One issue I have encountered is the repetitive ...

Why is it not possible to convert from any[] to MyType[] in TypeScript?

Within TypeScript, the any type allows for casting to and from any arbitrary type. For example, you can cast from a variable of type any to a variable of type MyArbitraryType like so: var myThing: MyArbitraryType; var anyThing: any; myThing = anyThing; / ...

Testing an asynchronous function in JavaScript can lead to the error message: "Have you neglected to utilize await?"

Here is an example of the code I am working with: public getUrl(url) { //returns URL ... } public getResponseFromURL(): container { let myStatus = 4; const abc = http.get(url, (respon) => const { statusCode } = respon; myStatus = statusCode ...

Maximizing the potential of mouse positioning in Angular

I am working with an Angular form that has a textarea <textarea class="form-control" id="message" formControlName="message" (fo ...

What is the best way to manage user sessions for the Logout button in Next.js, ensuring it is rendered correctly within the Navbar components?

I have successfully implemented these AuthButtons on both the server and client sides: Client 'use client'; import { Session, createClientComponentClient } from '@supabase/auth-helpers-nextjs'; import Link from 'next/link'; ...

Warning: Google Map API alert for outdated Marker Class

Working on an application using the Google Maps TypeScript API, I came across a warning message when launching the app -> As of February 21, 2024, google.maps.Marker will no longer be available. Instead, developers are advised to use google.maps.marke ...

Is it possible for a class that implements an interface to have additional fields not defined in the parent interface?

Looking at the code snippet below, my aim is to ensure that all classes which implement InterfaceParent must have a method called add that takes an instance of either InterfaceParent or its implementing class as input and returns an instance of InterfacePa ...

Error: There was a problem trying to import the `.d.ts` file

Software Stack Vue version 3.2.19 @vue/test-utils version 2.0.0-rc.15 Typescript version 4.1.6 Issue Description Encountering an error when running the command vue-cli-service test:unit --no-cache. Please refer to the TypeError screenshot (link to Con ...