Book Image

Hands-On Functional Programming with TypeScript

By : Remo H. Jansen
Book Image

Hands-On Functional Programming with TypeScript

By: Remo H. Jansen

Overview of this book

Functional programming is a powerful programming paradigm that can help you to write better code. However, learning functional programming can be complicated, and the existing literature is often too complex for beginners. This book is an approachable introduction to functional programming and reactive programming with TypeScript for readers without previous experience in functional programming with JavaScript, TypeScript , or any other programming language. The book will help you understand the pros, cons, and core principles of functional programming in TypeScript. It will explain higher order functions, referential transparency, functional composition, and monads with the help of effective code examples. Using TypeScript as a functional programming language, you’ll also be able to brush up on your knowledge of applying functional programming techniques, including currying, laziness, and immutability, to real-world scenarios. By the end of this book, you will be confident when it comes to using core functional and reactive programming techniques to help you build effective applications with TypeScript.
Table of Contents (14 chapters)
5
The Runtime – Closures and Prototypes

side-effects

In the preceding section, we learned that a pure function returns a value that can be computed using only the arguments passed to it. A pure function also avoids mutating its arguments or any other external variable that is not passed to the function as an argument. In FP terminology, it is common to say that a pure function is a function that has no side-effects, which means that, when we invoke a pure function, we can expect that the function is not going to interfere (through a state mutation) with any other component in our application.

Certain programming languages, such as Haskell, can ensure that an application is free of side-effects using their type system. TypeScript has fantastic interoperability with JavaScript, but the downside of this, compared to a more isolated language such as Haskell, is that the type system is not able to guarantee that our application is free from side-effects. However, we can use some FP techniques to improve the type safety of our TypeScript applications. Let's take a look at an example:

interface User {
ageInMonths: number;
name: string;
}

function findUserAgeByName(users: User[], name: string): number {
if (users.length == 0) {
throw new Error("There are no users!");
}
const user = users.find(u => u.name === name);
if (!user) {
throw new Error("User not found!");
} else {
return user.ageInMonths;
}
}

The preceding function returns a number. The code compiles without issues. The problem is that the function does not always return a number. As a result, we can consume the function as follows and our code will compile and throw an exception at runtime:

const users = [
{ ageInMonths: 1, name: "Remo" },
{ ageInMonths: 2, name: "Leo" }
];

// The variable userAge1 is as number
const userAge1 = findUserAgeByName(users, "Remo");
console.log('Remo is ${userAge1 / 12} years old!');

// The variable userAge2 is a number but the function throws!
const userAge2 = findUserAgeByName([], "Leo"); // Error
console.log('Leo is ${userAge2 / 12} years old!');

The following example showcases a new implementation of the preceding function. This time, instead of returning a number, we will explicitly return a promise. The promise forces us to then use the handler. This handler is only executed if the promise is fulfilled, which means that if the function returns an error, we will never try to convert the age to years:

function safeFindUserAgeByName(users: User[], name: string): Promise<number> {
if (users.length == 0) {
return Promise.reject(new Error("There are no users!"));
}
const user = users.find(u => u.name === name);
if (!user) {
return Promise.reject(new Error("User not found!"));
} else {
return Promise.resolve(user.ageInMonths);
}
}

safeFindUserAgeByName(users, "Remo")
.then(userAge1 => console.log('Remo is ${userAge1 / 12} years old!'));

safeFindUserAgeByName([], "Leo") // Error
.then(userAge1 => console.log('Leo is ${userAge1 / 12} years old!'));

The Promise type helps us to prevent errors because it expresses potential errors in an explicit way. In programming languages such as Haskell, this is the default behavior of the type system, but, in programming languages such as TypeScript, it is up to us to use types in a safer way.

We will learn more about Promises in Chapter 3, Mastering Asynchronous Programming. We will also learn more about how we can use a number of libraries to reduce the chances of side-effects in our TypeScript applications in Chapter 8, Category Theory.
If you find the idea of your JavaScript applications being free of side-effects attractive, you can try open-source projects such as https://github.com/bodil/eslint-config-cleanjs. This project is an ESLint configuration that aims to restrict you to a subset of JavaScript, which would be as close to an idealized pure functional language as possible. Unfortunately, at the time of publication, no similar tools are available that are specifically designed for TypeScript.