Photo by Melanie Pongratz on Unsplash

# Modernizing Examples: The nth Function

This is the 3rd installment in what is starting to become a series of posts where we modernize code examples from books. In the previous post, we built some utility functions to create a `parseAge`

function. The next example in the "Functional JavaScript" book uses those same utility functions to build an `nth`

function. So let's modernize this code together.

Here is the orginial examples:

```
function isIndexed(data) {
return _.isArray(data) || _.isString(data);
}
function nth(a, index) {
if (!_.isNumber(index)) fail("Expected a number as the index");
if (!isIndexed(a)) fail("Not supported on non-indexed type");
if (index < 0 || index > a.length - 1) fail("Index value is out of bounds");
return a[index];
}
function second(a) {
return nth(a, 1);
}
second("abc"); // b
nth([1, 2, 3], 0); // 1
nth({}, 2); // Error: Not supported on non-indexed type
```

The `nth`

function takes in an indexable value and an index. It does some guards to make sure you have actually passed in an indexable value and an index that is in bounds, and if everything is safe, it will return the value at the index. Since we already went over the `fail`

function in the previous post, I have left that code out.

Let's start with the `isIndexed`

function. This uses two utilities from underscore.js to confirm if the value is either an array or a string. There isn't an "array" type in JavaScript that you can check using the `typeof`

operator. If you call `typeof []`

, you will get "object." This is because arrays are just objects under the hood. In the past, we relied on utility functions, like `_.isArray`

to reliably check if value actually is an array and not just an object.

Luckily, we have a static method on the `Array`

object called `isArray`

that does this without needing a utility library. You can learn more about `Array.isArray`

over at MDN.. Also, in the last post, we created our own `assertIsString`

function. We can steal that logic and replace both of these checks, like this:

```
function isString(value) {
return toString.call(value) === "[object String]";
}
function isIndexed(data) {
return Array.isArray(data) || isString(data);
}
```

As for the `nth`

function, the only thing we have in there that should be updated is the `_.isNumber`

function from underscore. We can adapt our `isString`

function to be an `isNumber`

function with one tweak:

```
function isNumber(value) {
return toString.call(value) === "[object Number]";
}
```

So we can update the example to look like this:

```
function isNumber(value) {
return toString.call(value) === "[object Number]";
}
function isString(value) {
return toString.call(value) === "[object String]";
}
function isIndexed(data) {
return Array.isArray(data) || isString(data);
}
function nth(a, index) {
if (!isNumber(index)) fail("Expected a number as the index");
if (!isIndexed(a)) fail("Not supported on non-indexed type");
if (index < 0 || index > a.length - 1) fail("Index value is out of bounds");
return a[index];
}
function second(a) {
return nth(a, 1);
}
second("abc"); // b
nth([1, 2, 3], 0); // 1
nth({}, 2); // Error: Not supported on non-indexed type
```

Now, let's add types. Our `is`

functions are simple enough. Each one takes an unknown value and determines if it is something, so let's type them like this"

```
function isNumber(value: unknown) {
return toString.call(value) === "[object Number]";
}
function isString(value: unknown) {
return toString.call(value) === "[object String]";
}
function isIndexed(data: unknown) {
return Array.isArray(data) || isString(data);
}
```

Now let's add the types to our `nth`

function. In the previous post, I typed the function as unknown, partially because I wanted to keep the spirit of how the function was written, but partially so we could learn more about assertion functions. In a real TypeScript code base, we would write the types much more explicitly.

Our `nth`

function takes two arguments: `a`

and `index`

. `index`

is simple enough to type because it needs to be of type `number`

. `a`

could either be a string or an array. So first, let's create a type called `Indexed`

:

```
type Indexed = Array<unknown> | string;
```

This essentially says, that `Indexed`

could be an array of unknown values or a string. Now we could do something like this:

```
function nth(a: Indexed, index: number) {}
```

But this will force the value of `a`

to be an array of unknown values if we pass in an array. It would be much nicer to let TypeScript infer what type of array we are passing in. Luckily we can do that using generics. Let's look at the updated types:

```
function nth<T extends Indexed>(a: T, index: number): T[number] {}
```

Let's break that down. First, we are saying that `nth`

takes a generic value of `T`

that must extend the `Indexed`

type we declared. Then we say that `a`

is of type `T`

and that we are returning `T[number]`

or the value at the index of `T`

.

By having `T`

extend `Indexed`

, we are now letting Typescript infer the array's values so it can then accurately know what the return type is. For example, if we call `nth([1,2,3], 0)`

, Typescript will know that the return type is of type number.

The `second`

function below can be typed the same way, except it wont need an explicit return type since it can infer that from the `nth`

function we are calling. So now let's update the entire example using TypesScript:

```
function isNumber(value: unknown) {
return toString.call(value) === "[object Number]";
}
function isString(value: unknown) {
return toString.call(value) === "[object String]";
}
function isIndexed(data: unknown) {
return Array.isArray(data) || isString(data);
}
type Indexed = Array<unknown> | string;
function nth<T extends Indexed>(a: T, index: number): T[number] {
if (!isNumber(index)) fail("Expected a number as the index");
if (!isIndexed(a)) fail("Not supported on non-indexed type");
if (index < 0 || index > a.length - 1) fail("Index value is out of bounds");
return a[index];
}
function second<T extends Indexed>(a: T) {
return nth(a, 1);
}
second("abc"); // b
nth([1, 2, 3], 0); // 1
nth({}, 2); // TypeError
```

There you have it, a fully modernized and typed example from the classic "Functional JavaScript." I highly recommend checking it out, and I'll see you next time.