January 24, 2023

TypeScript - Function as a parameter

Sometimes we need to pass a function as a parameter to another function, which will internally call the parameter function.

Lets see an example how we can receive a function as a parameter.

We have to define the parameter type as Function.

function shipOrder(func: Function): void {
   //some logic
   console.log("shipOrder function is called");
	
   //call the function which is passed a parameter
   func();
}

Lets say we have a function getBillingAddress as follows:

getBillingAddress() {
   //some logic
   console.log("getBillingAddress function is called");
}

shipOrder is the function which will accept another function as a parameter, and then internally call this function. Since getBillingAddress accepts not parameter and returns void, we can simply invoke the function by its name alongwith paranthesis.

This is how we will call the shipOrder function by passing the function name as parameter.

shipOrder(getBillingAddress);

We can make it easier to read, define an interface describing the function signature that need to pass as paramter:

interface IFunction {
(): void;
}

The shipOrder function will become like this:

function shipOrder(func: IFunction): void {
   //some logic
   console.log("shipOrder function is called");
	
   //call the function which is passed a parameter
   func();
}

Since IFunction interface contains a function, with not paramters and void return type. Passing our old getBillingAddress function is still valid because of same function signature.

shipOrder(getBillingAddress);

We can also specify the paramters and return type for the function. Lets define a new interface:

interface INumberFunction {
(num: number): string;
}

Change the paramter type to INumberFunction

function shipOrder(func: INumberFunction): void {
   //some logic
   console.log("shipOrder function is called");
	
   //call the function which is passed a parameter
   //now the func is of type INumberFunction, we need to pass a number paramter and it will return a string.
   let someValue: string = func(1);
}

Calling the function and passing parameter is same.

shipOrder(getBillingAddress);

But when you need to invoke the actual function (which is passed a paramter), you have to take care of its signature, the required paramters and return type.

   let someValue: string = func(1);

C# Yield Return & Break Statements

The yield keyword makes the method (in which it appears) as an iterator block. An iterator block, or method, will return an IEnumerable as the result. Within this iterator block or method, we will use the yield keyword to return the values for the IEnumerable.

Note that IEnumerable is lazily evaluted. If you call a method with an iterator block (with yield keyword), it will not run any code until we actually access the IEnumerable result values.

Lets see an exmaple.

Usually you may find code similiar to this where we are creating a temp list to hold the items, and at the end return the list from a method:

public IEnumerable<int> MyList()
{
	List<int> list = new List<int>();
	list.Add(1);
	list.Add(2);
	list.Add(3);
	list.Add(4);
	list.Add(5);

	return list;
}

You can simplify the method using the yield return statement, it allows us to remove the intermediate/temporary list required to hold the values.

public IEnumerable<int> MyList()
{
	yield return 1;
	yield return 2;
	yield return 3;
	yield return 4;
	yield return 5;
}

This method will return the same list of intergers but it does not need a temporary list to hold values.

You can use the yield break statement to explicitly stop the current iteration and cancel the iterator block. In this case it will return all these values which are produced with yield return statement. Once it reaches the yield break statement, it will stop producing any further value and exit the iterator block.

Lets see this example:

public static IEnumerable<int> MyList()
{
	yield return 1;
	yield return 2;
	yield break;
	yield return 3;
	yield return 4;
	yield return 5;
}

This method will only produce two values 1 and 2. Once it reaches the yield break statement, it will exit from the iterator block.

Typically you would do this when a certain condition is met, you only want to return a specific set of values from the iterator block.

Let see this example:

IEnumerable TakeWhilePositive(IEnumerable<int> numbers)
{
    foreach (int n in numbers)
    {
        if (n > 0)
        {
            yield return n;
        }
        else
        {
            yield break;
        }
    }
}

If you call this method like:

foreach (var item in TakeWhilePositive(new[] { 1, 2, 3, -1, 4, 5}))
{
	Console.WriteLine(item);
}

It will print the values from given array as:

1
2
3

Once it found a negative value, the iteration loop will be stopped and control is returned to the caller method.

References: