Skip to content

Hands‑On Exercise: Type a Small Array Utility Library

In this exercise, you’ll take a small JavaScript “array helpers” module and progressively add TypeScript types to make it safer, clearer, and more maintainable.

The goal is to show how TypeScript transforms everyday JavaScript into a robust, self‑documenting API.


Step 1 — Start with a plain JavaScript utility file

Create a file named arrayUtils.js:

// arrayUtils.js

export function first(arr) {
  return arr[0]
}

export function last(arr) {
  return arr[arr.length - 1]
}

export function findById(arr, id) {
  return arr.find(item => item.id === id)
}

export function chunk(arr, size) {
  const result = []
  for (let i = 0; i < arr.length; i += size) {
    result.push(arr.slice(i, i + size))
  }
  return result
}

This works in JavaScript, but it has problems:

  • No guarantee that arr is an array
  • No guarantee that items have an id
  • chunk returns arrays of arrays, but nothing enforces that
  • No autocomplete or IntelliSense for consumers

Now let’s convert it to TypeScript.


Step 2 — Rename the file to TypeScript

arrayUtils.js → arrayUtils.ts

Immediately, TypeScript will start complaining—good!

This is where the learning happens.


Step 3 — Add parameter and return types

Start by typing the simplest functions.

first and last

export function first<T>(arr: T[]): T | undefined {
  return arr[0]
}

export function last<T>(arr: T[]): T | undefined {
  return arr[arr.length - 1]
}

Concepts used:

  • Generic type parameter <T>
  • Typed arrays (T[])
  • Union return type (T | undefined)

In the code above we introduced generics. We haven’t talked about generics yet, and we will introduce them in a separate lesson. For now, consider them as generic type placeholders. In the example above, first<T>(arr: T[]) takes an array of type T and returns : T | undefined, a value of type T or undefined.


Step 4 — Type the findById function

We want to enforce that:

  • arr is an array of objects
  • each object has an id property
  • id can be a string or number
export function findById<T extends { id: string | number }>(
  arr: T[],
  id: string | number
): T | undefined {
  return arr.find(item => item.id === id)
}

Concepts used:

  • Type constraint (T extends { id: ... }) (once again, this will be covered in the coming lessons)
  • Typed arrays
  • Union types

Step 5 — Type the chunk function

chunk returns an array of arrays, so we use generics again.

export function chunk<T>(arr: T[], size: number): T[][] {
  const result: T[][] = []
  for (let i = 0; i < arr.length; i += size) {
    result.push(arr.slice(i, i + size))
  }
  return result
}

Concepts used:

  • Nested arrays (T[][])
  • Explicit return type
  • Typed intermediate variables

Step 6 — Add a tuple‑based helper (optional bonus)

Let’s add a helper that returns both the min and max of a numeric array.

export function minMax(arr: number[]): [min: number, max: number] {
  const min = Math.min(...arr)
  const max = Math.max(...arr)
  return [min, max]
}

Concepts used:

  • Tuples
  • Named tuple elements

Step 7 — Test the library

Create a file test.ts:

import { first, last, findById, chunk, minMax } from "./arrayUtils"

const nums = [10, 20, 30]

first(nums)      // number | undefined
last(nums)       // number | undefined
chunk(nums, 2)   // number[][]

const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" }
]

findById(users, 2) // { id: number; name: string } | undefined

minMax(nums) // [number, number]

Hover over each function call in your editor—you’ll see TypeScript’s type inference in action.


Step 8 — Observe compiler feedback

Try breaking things intentionally:

first("not an array")     // ❌ Error
findById(users, true)     // ❌ Error
chunk(nums, "two")        // ❌ Error
minMax(["a", "b"])        // ❌ Error

TypeScript catches all of these mistakes before runtime.


What You Learned

This exercise demonstrates how TypeScript transforms a simple utility library into a safe, expressive API.

Concepts applied:

  • Generic functions
  • Typed arrays
  • Tuple return types
  • Type constraints
  • Union types
  • Explicit return types
  • Compiler‑driven feedback

Benefits gained:

  • Safer code
  • Better autocomplete
  • Clearer API contracts
  • Fewer runtime bugs
  • Easier refactoring

This is exactly how TypeScript improves real‑world JavaScript codebases.