pank
pank

Reputation: 943

Typescript generic for function with a variable number of parameters

I have a function that takes an arbitrary number of classes.

function findObjects(...Classes) { ... }

All classes inherit from one Component class. This function in a certain way searches for objects that are instances of these classes.

How to implement a template for functions with a variable number of parameters?

It should be something like this:

function findObjects(...Classes): [...Classes] { ... }

This is necessary for the correct definition of types and for the correct operation of intellisense.

Upvotes: 3

Views: 8547

Answers (3)

tony
tony

Reputation: 1326

You can use rest parameters as described in the docs

interface A { }

class B implements A { }


class C implements A { }


function findObjects(...objs: A[]): A[] {
  // function body

}

const obj1: B = {}
const obj2: C = {}

const result1 = findObjects(obj1);
const result2 = findObjects(obj1, obj2);

Hope that helps!

Upvotes: 1

Maciej Sikora
Maciej Sikora

Reputation: 20132

My answer follows what you have written:

All classes inherit from one Component class. This function in a certain way searches for objects that are instances of these classes.

So in the solution I am using some base class Component and to search for some subclass members SubComponent.

// some empty classes
class Component {}
class SubComponent extends Component {}

function findObjects(...args: Component[]): SubComponent[] { 
  return args.filter(el => el instanceof SubComponent); // do some by instanceof for example
}
// using
const results = findObjects(new Component(), new Component(), new SubComponent());
// results is an array only with SubComponent element

The definition of the function is following - findObjects(...args: Component[]): SubComponent[].

  • ...args: Component[] - we take as arguments unknown number of Component instances
  • : SubComponent[] - we return array of SubComponents

I would propose to change little bit definition of this, as we return array, then better would be to take also array:

function findObjects(arr: Component[]): SubComponent[] { 
  return arr.filter(el => el instanceof SubComponent); // do some instanceof for example
}
// using
const results = findObjects([new Component(), new Component(), new SubComponent()]);
// results is an array only with SubComponent element

Pay attention that now the argument is one - arr but it is consistent with the output, we put array in, and take array out. Just a suggestion though.

Upvotes: 1

ford04
ford04

Reputation: 74500

You may want to have a look at tuple rest parameters, which are used to emulate functions with a variable number of parameters. A possible solution:

// We pass in a tuple of classes. This tuple is generically typed (type parameter T)
// { new(...arg: any): any } is constructor function type for a class
function findObjects<T extends { new(...arg: any): any }[]>(...cls: T): Partial<T> {
  // search for objects that are instances of these classes; return filtered classes
}

class Component { c = "c" }
class A extends Component { a = "a" }
class B extends Component { b = "b" }

const res = findObjects(A, B) // [(typeof A | undefined)?, (typeof B | undefined)?]

For the return type I used Partial<T>, which is a mapped tuple with optional elements of the passed in classes. It contains undefined at an index, if the corresponding class item is filtered out by your findObjects implementation (adjust the return type like you need it).

Playground

Upvotes: 2

Related Questions