Data Structures

Exploring Array Data Structure in TypeScript

Arrays are the data structure you use without thinking about it. They're everywhere. And that familiarity hides some important performance characteristics.

20 Apr 2024

Exploring Array Data Structure in TypeScript

Arrays are the data structure you use without thinking about it. They're everywhere. And that familiarity hides some important performance characteristics.

An array stores elements in contiguous memory locations. This means you can access any element by index in O(1) time. But inserting or removing from the middle requires shifting every subsequent element -- O(n).

Creating Arrays in TypeScript

Typescript
const numbers: number[] = [1, 2, 3, 4, 5];
const fruits: string[] = ['apple', 'banana', 'orange'];
const colors: Array<string> = ['red', 'green', 'blue'];

TypeScript gives you two syntax options. number[] and Array<number> are identical. I prefer the shorter form.

Accessing Elements

Array access by index is O(1). That's the core strength.

Typescript
console.log(numbers[0]); // 1
console.log(numbers[numbers.length - 1]); // 5

Out-of-bounds access returns undefined in JavaScript/TypeScript. No error, no crash. Just a silent undefined that causes bugs downstream.

Iteration

Three common approaches:

Typescript
// Classic for loop -- gives you the index
for (let i = 0; i < numbers.length; i++) {
  console.log(numbers[i]);
}

// forEach -- clean, but can't break out early
fruits.forEach((fruit) => {
  console.log(fruit);
});

// for...of -- modern, supports break/continue
for (const color of colors) {
  console.log(color);
}

Use for...of by default. Use forEach when you don't need early exit. Use a classic for loop when you need the index or performance in tight loops.

Adding and Removing Elements

Typescript
// O(1) at the end
numbers.push(6);
numbers.pop();

// O(n) at the beginning -- shifts everything
fruits.unshift('mango');
fruits.shift();

push and pop operate at the end of the array -- O(1). unshift and shift operate at the beginning -- O(n) because every element must move. If you're frequently adding/removing from the front, consider a different data structure like a linked list or deque.

Slice vs Splice

These two get confused constantly.

slice returns a new array without modifying the original:

Typescript
const sliced = numbers.slice(2, 4); // elements at index 2 and 3
// numbers is unchanged

splice mutates the original array:

Typescript
numbers.splice(1, 2, 8, 9); // remove 2 elements at index 1, insert 8 and 9
// numbers is modified in place

Performance Summary

Operation Time Complexity
Access by index O(1)
Search (unsorted) O(n)
Push / Pop (end) O(1)
Shift / Unshift (start) O(n)
Insert / Delete (middle) O(n)

Arrays are the right choice when you need fast random access and mostly append to the end. They're the wrong choice when you need frequent insertions and deletions at arbitrary positions. Know the trade-offs, and the array serves you well.

Keep reading