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

Modify an array by incorporating values from another array that have a matching property

I have two arrays that look like this let array1 = [{ 'id': 1, 'name': 'A' }, { 'id': 2, 'name': 'B' }, { 'id': 3, 'name': ' ...

Choosing the Active Browser Tab while Modal is Open in Angular

In my current situation, I have implemented a function in the first tab that displays a modal or component after 5 seconds: ngOnInit() { setTimeout(() => { this.openDialog(); }, 5000); } openDialog() { this.dialog.open(.....); } However, if ...

How can Node / Javascript import various modules depending on the intended platform?

Is there a way to specify which modules my app should import based on the target platform in TypeScript? I am interested in importing different implementations of the same interface for a browser and for Node.js. In C++, we have something like: #ifdef wi ...

I encountered an issue with my TypeScript function in Angular, as it is unable to process multiple uploaded files

I'm having trouble with my TypeScript function in Angular that is unable to read multiple uploaded files. fileUpload(event: Event) { const self = this; this.imageUploadInp = event.target as HTMLInputElement; this.imageUploadInp.addEventLis ...

What is the process for including a new item in the p-breadcrumb list?

Having trouble getting my code to add a new item to the p-breadcrumb list on click. Any assistance would be greatly appreciated. Thank you in advance! Check out the live demo here ngOnInit() { this.items = [ {label: 'Computer'}, ...

Numerous unspecified generic arguments

Imagine a collection of functions, each capable of taking an argument and returning a value (the specifics don't matter): function convertToNumber(input: string): number { return parseInt(input) } function convertToBoolean(input: number): boolean { ...

Ways to implement material-ui button design on an HTML-native button

I am using pure-react-carousel which provides me an unstyled HTML button (ButtonBack). I would like to customize its style using material-ui. Trying to nest buttons within buttons is considered not allowed. An approach that works is manually assigning th ...

Navigating through a JSON dictionary in Svelte: A step-by-step guide

When working with Svelte, the #each construct allows for easy iteration over array-like objects. But what if you have a JSON dictionary object instead? Is there a way in Svelte to iterate over this type of object in order to populate a dropdown menu? The ...

Include module A in module B, even though module A has already included module B's Angular

Currently, I am facing an issue with circular dependencies between two modules - User and Product. The User Module has already imported the Product Module to utilize the ProductListComponent. Now, I need to import/use the UserListComponent from the User Mo ...

Determining User Existence in AWS DynamoDB with Node.js Before Creating New Record

Currently, I am in the process of developing an AWS Lambda function to create a new customer (lead). However, prior to the creation of the customer, there is a need to perform a check to determine if the user already exists. The identification of a custome ...

How can I customize a Vue component slot in Storybook 8.0.6 using Vue 3.4 and Typescript to display various subcomponents within a story?

Incorporating a variety of sub-components into my Vue 3 component based on context is proving to be a challenge. Utilizing slots seems to be the solution in Vue 3, but I'm struggling to make it work within Storybook 8, which I'm using to showcase ...

Can discriminated unions solely be utilized with literal types?

When looking at the code snippet below, I encountered an issue with discriminating the union type using the typeof operator. function f(arg: { status: number; one: boolean } | { status: string; two: boolean }) { if (typeof arg.status === "number&q ...

A step-by-step guide on integrating Azure Cognitive Search within a SharePoint Online SPFx webpart

I haven't had much experience with SPFx recently, so it looks like I'll need to brush up a bit. :) I'm interested in implementing this NPM package: https://www.npmjs.com/package/azure-search Within a simple new SPFx web part: The code ...

Encountering a "args" property undefined error when compiling a .ts file in Visual Studio Code IDE

I've created a tsconfig.json file with the following content: { "compilerOptions": { "target": "es5" } } In my HelloWorld.ts file, I have the following code: function SayHello() { let x = "Hello World!"; alert(x); } However ...

I am verifying the user's login status and directing them to the login page if they are not already logged in

My goal is to utilize ionViewWillEnter in order to verify if the user is logged in. If the check returns false, I want to direct them to the login page and then proceed with the initializeapp function. My experience with Angular and Ionic is still limite ...

Exploring arrays within objects with Angular

REACT: this.countries = this.api.fetchAllCountries(); this.countries.forEach(item => { this.countryData.push(item); }); VUE: <div v-for="country in countryData" @click="displayCountryInfo(country ...

Exploring the wonders of Angular 2: Leveraging NgbModal for transclusion within

If I have a modal template structured like this: <div class="modal-header"> <h3 [innerHtml]="header"></h3> </div> <div class="modal-body"> <ng-content></ng-content> </div> <div class="modal-footer"& ...

Typescript compiles only the files that are currently open in Visual Studio

In my Visual Studio Typescript project, I am in the process of transforming a large library of legacy JavaScript files by renaming them to *.ts and adding type information to enhance application safety. With over 200 files to modify, it's quite a task ...

The module 'SharedModule' has imported an unexpected value of 'undefined'

When working with an Angular application, I want to be able to use the same component multiple times. The component that needs to be reused is called DynamicFormBuilderComponent, which is part of the DynamicFormModule. Since the application follows a lib ...

Challenges in designing components in Angular 2.0 and beyond

Issue at hand - There are two input controls on the same page, each belonging to separate components. When a value is entered into the first input box, it calculates the square value and updates the second input control accordingly. Conversely, if the v ...