Contents

A Practical Guide to First-Class Functions Using TypeScript With Node.js

A Practical Guide to First-Class Functions Using TypeScript With Node.js

Learn and implement a core concept of functional programming using TypeScript.

https://cdn-images-1.medium.com/max/800/1*a6pT7-Y8n2BimS_-iKEaIw.jpeg

Source: Unsplash


Why First-Class Functions?

Functional programming has been increasingly becoming popular as a programming paradigm. LISP and Haskell are leading the way. However, we, the full-stack developers, are lucky that JavaScript natively supports its features.

One of the core concepts of functional programming is*first-class functions.*Meaning, we are treating functions as first-class citizens. It offers us flexibility in implementing functions by treating them just like any other value (objects, strings, and numbers), passing them as arguments, or returning them from other functions in our code. Understanding first-class functions is an essential foundation for learning and implementing the functional programming paradigm. The end goal is for us to write maintainable, flexible, and scalable code.

In this article, I’m going to walk you through the different features of a first-class function. I will share some examples, sample problems, and solutions. Note: I wrote all my examples in TypeScript and tested them with Node 10.


Arrow Functions — Shorthand

I will use the shorthand for expressing functions in ES6 by default for the rest of this article. So that our code will be more concise, let’s set the tone by reviewing and understanding how to write arrow functions in ES6. Feel free to skip this part if you are already familiar with arrow functions.

We would typically use thefunctionkeyword when we wrote functions before ES6, for example, if we wanted to write a function that returns the winning percentage. We wrote the function and named it, or we assigned it to a variable by usingconst,var, orlet.

View Code on GitHub Gist

Named functions in TypeScript

Starting with ES6, we can rewrite our function to an arrow function as in the example below.

View Code on GitHub Gist

Arrow function introduced in ES6

We do not need to write thefunctionkeyword. The arrow=>is pointing to the set of instructions in our function enclosed in curly braces. In TypeScript, we can also specify that a variable is aFunctiontype if we want to.

The example below returns the result directly without keeping it in our variable. This further shortens our code.

View Code on GitHub Gist

One-liner with an arrow function

Notice how the arrow=>above tells the interpreter that we want to return the value of the statement it’s pointing to, which is the result when we dividematchesbywins, as long as we don’t enclose the statement in curly braces.

We need to enclose our arguments in parentheses in TypeScript if we specify the type, unlike when using JavaScript arrow functions. We can omit the parentheses if we only need to pass one argument.

View Code on GitHub Gist

Arrow function with one typed argument

Use an empty parenthesis if there is no argument passed.

View Code on GitHub Gist

Arrow function without an argument

If we want to return an object, enclose it in parentheses. If we don’t enclose it in parentheses, we tell the interpreter that we are using the brackets to define a function’s body.

View Code on GitHub Gist

Enclose the object in parenthesis if you want to return an object.

Writing functions in this shorthand form allows us to “compress” our code, saving us from extra keystrokes when typing and additional characters when reading. Writing functions in shorthand form can be a little confusing in the beginning; however, there’s compounding interest to gain here in the long term. Aside from saving us from extra keystrokes and redundant use of the wordfunction, it makes it convenient for us to write our TypeScript code in a functional programming style. How? Arrow functions resemble components of a mathematical equation, and that keeps our code concise and less cluttered.


Treating Functions as Data

We can treat functions the same as we treat other TypeScript data types, like numbers, strings, arrays, and objects. We can store a function in a variable and treat it just like any other TypeScript data type, which gives us a few useful options for what we can do with our functions.

Here’s an example of assigning our function to another variable.

View Code on GitHub Gist

Create a function and assign it to another variable.

Treating functions like the other data types also allows us to change the behavior of a function dynamically.

View Code on GitHub Gist

Use a ternary operator to change the function behavior dynamically.

Here are some typical real-world applications of this feature.

Example usage 1. Mocking in unit tests

One common application of this feature is to mock what our async function returns when running unit tests locally.

View Code on GitHub Gist

Example usage 2. A/B testing or experimentation

We can also assign our functions to an object to form a hash map or dictionary of functions that we can use for our A/B tests.

View Code on GitHub Gist

Example usage 3. Execute a series of functions to a data

Here’s another interesting application. We have many functions that take an object, execute a series of updates to that object, and return an updated object.

const addName = (person: object): object => ({
    ...person,
    name: 'John Doe'
})

const addGender = (person: object): object => ({
    ...person,
    gender: 'Male'
})

const addJobTitle = (person: object): object => ({
    ...person,
    title: 'Software Engineer'
})

const addMonthlySalary = (person: object): object => ({
    ...person,
    monthlySalary: 1000
})

const addAnnualSalary = (person: object): object => ({
    ...person,
    annualSalary: person['monthlySalary'] * 12
})

The procedural way to do this is to apply the function line by line. For example, we have anemployeeobject initialized with anid. Then a series of functions will update theemployeeobject and return the updatedemployeeobject. See the example code below.

View Code on GitHub Gist

Take the person object and update it.

The code above gets the job done. We are applying each function line by line to the mutatingemployeeobject. Imagine if we need to add a new function: Add a new line to call the function and pass theemployeeobject as an argument. Not bad, but we can do better.

The functional way is to assign the functions in an array, iterate through that array usingforEach, and apply each function to the argument. Note theemployeeargument to pass after applying the first function in the array will be the returned value of the previous function. We use the functions in the same order they are positioned in the array.

View Code on GitHub Gist

Take the person object and update it using an array of functions.

If we need to add another function to execute, we only need to add another entry in the array. We do not need to reassign the returned value toemployeeand pass it as an argument to the next function.


Functions as Arguments

Going back to the theme of dynamically changing a function’s behavior, let’s look at how to achieve that by passing functions as arguments.

For example, we want to implement a simple calculator that accepts two numbers. The object-oriented approach is to create aCalculatorclass that contains the calculation operations as methods within that class, which areaddandsubtract. If we need to add more operations, we will update the class to add each operation’s corresponding method. Another way is to write an individual function for each operation. For instance, implement thecalculateSum()andcalculateDifference()functions.

The “first-class function” or “functional” way to implement the calculator here will be to create acalculate()function that accepts an operation function, e.g.add(), as an argument that will perform the calculation we want. The function that calculates is also self-contained, and we can use them without them being passed as an argument.

View Code on GitHub Gist

Pass the operation function as an argument to the calculate function.


Functions as Return Values

Seeing code that returns a function from another function can be confusing in the beginning. It helps to think of it as a factory of functions.

An easier way to understand this concept is to start with a function that returns another function that prints text to the console. The function below returns a function that prints “hello” to the console.

const createGreeting = () => () => console.log('hello')

It might take some time to get used to reading this type of function if you’re new to this. Keep in mind that this function is the equivalent of the following function.

const createGreeting = function() {
    return function() {
        console.log('hello')
    }
}

Let’s look at an example. We have a program with conversion functions that convertmeterinto the imperial unitsmile,yard,footandinch. These unit conversion functions take the valuexin meter and return the converted value in an imperial unit formatted with its corresponding symbol.

View Code on GitHub Gist

Conversion functions with repetitive code

The duplicated code in our conversion functions is quite evident in the example above. Multiplication and string formatting in each function is repetitive.

Let’s try to solve this by creating a function that will handle the different conversions for us while maximizing code reuse between functions with slightly different behaviors.

ThecreateConverter()function below accepts the conversion valuevand symbolsarguments. It returns a function that takes anxargument, which is the value that we want to convert.

const createConverter = (v: number, s: string) => (x: number) => `${x * v} ${s}`

We will then reuse thecreateConverterfunction above to form our conversion functions.

const meterToMile = createConverter(0.0006213689, 'mi')
console.log(meterToMile(1000))

Here is our full solution below. We are maximizing reuse while keeping our code maintainable.

View Code on GitHub Gist

Conversion functions with reusable code. We are utilizing functions as return values.


Closures

Since we’ve covered returning a function from another function, we should understand the variables’ scope within the function that returns a function. The returning function’s variables are accessible within the returned function.

The variable_namein thecreateGreeting()function below is accessible from the returned function that prints to the console. It will print outHello John Doewhen we run it.

View Code on GitHub Gist

Function with closure


Private Variables

This section will combine what we have learned about returning functions and closures in the previous sections. Usually, in JavaScript, we will append our private variable with an underscore to indicate that we should not use the variable anywhere else outside its class. Sometimes this can be ignored or misused. We can enforce how a class in object-oriented programming handles private variables by using a functional approach. Instead of usingclassin our example, we will implement it as a function.

Take a look at the example below. We have anEmployeefunction that accepts thename,jobTitle,andmonthlySalaryarguments. The function assigns these arguments to the private variables appended with an underscore to indicate that they are private. We can only access these variables through our getters:getName(),getJobTitle(), andgetEmployee(). If we attempt to retrieve the private variableemployeeA._namedirectly, we will get aProperty ‘_name’ does not existerror.

View Code on GitHub Gist

Employee function with private variables and getters

We can also expand ourEmployeefunction to use setters. We have added the setters in the example below.

View Code on GitHub Gist

Private variables with setters

The last two lines in the code above will output our employee’s updated job title and salary.

Please note: You do not always need to write your “class” like this in TypeScript because TypeScript already supports the implementation of private variables in a class. It’s good to know that you have the option to implement your private variables this way.


Higher-Order Functions

A higher-order function is a function that operates on other functions by taking any function as an argument or by returning them. Let’s go through an example of how we can use higher-order functions to give us flexibility and reusability in our code.

To illustrate this in an example, let’s take a look at a common problem that we solve as programmers: validating our function arguments.

We have a function that performs a subtraction operation, given aminuendand asubtrahendas its arguments, and returns thedifference.

const minus = (minuend: number, subtrahend: number): number => minuend - subtrahend

Minuend — This is the biggest number, or the whole, from which apartwill be taken away.

Subtrahend — This is thepartthat is taken away from the minuend.

Difference — This is thepartthat is left-over aftersubtraction. [1]

Let’s try to limit it to return non-negative numbers only. Therefore we will need our arguments such that theminuendshould always be greater than or equal to thesubtrahend:minuend >= subtrahend.

The typical way to validate the arguments will be to add an early return or exit on top of the function.

const minus = (minuend: number, subtrahend: number): number => { if (minuend < subtrahend) { console.log('minuend should be greater than the subtrahend') return null } return minuend - subtrahend}

The code above works. However, the function is doing more than one thing. Instead of returning the difference only, it also validates the arguments. It violates the single-responsibility principle in S.O.L.I.D.[2]

Let’s solve that by using higher-order functions.

First, we’ll create a separate function for validating the arguments.

const minuendGreaterThanSubtrahend = (func: Function) => (...args) => { const [num1, num2] = args if (num1 < num2) { console.log('minuend should be greater than the subtrahend') return null } return func(...args) }

The functionminuendGreaterThanSubtrahend()accepts a function passed as an argument. It then validates the arguments that will be called from the function passed.

Let’s remove the validation in ourminus()function, getting it back to its original form that complies with the single-responsibility principle.

const minus = (minuend: number, subtrahend: number): number => minuend - subtrahend

We will then create theminusPositiveDifference()function that calls theminuendGreaterThanSubtrahend()validation function with theminus()function as its argument. ThisminusPositiveDifference()will then be called to ensure that our arguments are validated first before performing the subtraction.

const minusPositiveDifference = minuendGreaterThanSubtrahend(minus)

See our full solution below. Each function is doing one thing while still achieving the argument validation that we want.

View Code on GitHub Gist

A functional approach to validate arguments while keeping our code compliant with the single-responsibility principle


Conclusion

Understanding first-class functions in JavaScript (TypeScript) is crucial to your functional programming journey. It helps us approach problems in exciting ways while utilizing the power and flexibility of first-class functions. You are going to encounter problems with patterns similar to the examples in this article. The more you use these first-class function features in your projects, the more you’ll master them.


References

  1. Subtraction equations[Back to text]
  2. S.O.L.I.D. Principles.[Back to text]

If you want to run the sample code in this article, it is also available onGitHub.