By Language
Data Structures in TypeScript
12 essential data structures for TypeScript developers. Each one explained with Big O complexity, animated visuals, and real code samples you can copy.
Array
T[]Fixed-size, contiguous block of memory. Elements are stored sequentially and accessed by index in constant time. The foundation of most other data structures.
arr[0] = 7
Complexity
When to use
- +You know the exact size at creation time
- +You need the fastest possible index-based access
- +Working with fixed-length data like matrices or buffers
// Fixed-length tuple (closest to fixed array)
const primes: readonly number[] = [2, 3, 5, 7, 11] as const;
// Typed array - true fixed-size contiguous memory
const buf: Int32Array = new Int32Array(5); // O(1)
buf[0] = 42; // O(1) access
buf[3] = 99; // O(1) assign
// Search
const idx: number = buf.indexOf(99); // O(n) -> 3
// Iterate
for (const val of buf) console.log(val);
// Sort (returns new array in ES2023)
const sorted: Int32Array = buf.toSorted(); // O(n log n)
console.log(buf.length); // 5 - fixedDynamic Array
T[]Resizable array that automatically grows when capacity is exceeded. The most commonly used data structure in most languages. Doubles its internal storage when full, giving amortized O(1) appends.
arr[0] = 7
Complexity
When to use
- +You need a resizable collection (most common case)
- +You frequently access elements by index
- +You mostly add or remove at the end
// Array<T> IS the dynamic array in TypeScript
const nums: number[] = [10, 20, 30];
nums.push(40); // O(1) amortized
nums[1] = 25; // O(1) index access
nums.splice(2, 0, 28); // O(n) insert at index 2
nums.splice(1, 1); // O(n) remove at index 1
const has: boolean = nums.includes(40); // O(n)
const idx: number = nums.indexOf(28); // O(n) -> 1
nums.sort((a, b) => a - b); // O(n log n)
// Type-safe generics
interface Stack<T> { items: T[]; push(item: T): void; }Stack
T[] (push/pop)Last-In-First-Out (LIFO) collection. Only the top element is accessible. Used for tracking state that must be unwound in reverse order.
Push 10
Complexity
When to use
- +Undo/redo functionality
- +Expression parsing and evaluation
- +DFS traversal of trees and graphs
- +Matching brackets, parentheses validation
// Array as stack with type safety
const stack: number[] = [];
stack.push(10); // O(1)
stack.push(20);
stack.push(30);
const top: number | undefined = stack.at(-1); // O(1) peek
const val: number | undefined = stack.pop(); // O(1)
// Interview pattern: valid parentheses (typed)
function isValid(s: string): boolean {
const st: string[] = [];
const pairs: Record<string, string> = { ")": "(", "]": "[", "}": "{" };
for (const c of s) {
if (!pairs[c]) st.push(c);
else if (st.pop() !== pairs[c]) return false;
}
return st.length === 0;
}Queue
T[] (shift/push)First-In-First-Out (FIFO) collection. Elements are added at the back and removed from the front. Fundamental for breadth-first processing.
Enqueue 10
Complexity
When to use
- +BFS traversal of trees and graphs
- +Task scheduling and job queues
- +Message passing between components
- +Rate limiting, buffering
// Array-based queue (shift is O(n) - fine for interviews)
const queue: string[] = [];
queue.push("A"); // O(1) enqueue
queue.push("B");
queue.push("C");
const front: string | undefined = queue[0]; // O(1) peek
const next: string | undefined = queue.shift(); // O(n) dequeue
// Interview pattern: BFS with types
interface TreeNode { val: number; left?: TreeNode; right?: TreeNode; }
function bfs(root: TreeNode): number[] {
const result: number[] = [];
const q: TreeNode[] = [root];
while (q.length > 0) {
const node = q.shift()!; // O(n) dequeue
result.push(node.val);
if (node.left) q.push(node.left);
if (node.right) q.push(node.right);
}
return result;
}Hash Map
Map<K,V>Maps keys to values using a hash function for near-constant-time lookups. The single most important data structure for coding interviews. Every language has a built-in implementation.
hash("age") = 0
Complexity
When to use
- +Two Sum and frequency counting patterns
- +Caching computed results (memoization)
- +Grouping data by a key
- +Any problem requiring O(1) lookup by key
// Map<K, V> - fully typed key-value store
const map = new Map<string, number>();
map.set("apple", 3); // O(1)
map.set("banana", 5);
map.set("apple", 10); // O(1) update
const val: number | undefined = map.get("apple"); // O(1)
const has: boolean = map.has("banana"); // O(1)
map.delete("banana"); // O(1)
// Interview pattern: Two Sum
function twoSum(nums: number[], target: number): [number, number] | null {
const seen = new Map<number, number>();
for (let i = 0; i < nums.length; i++) {
const need = target - nums[i];
if (seen.has(need)) return [seen.get(need)!, i];
seen.set(nums[i], i);
}
return null;
}Hash Set
Set<T>Unordered collection of unique elements. Uses hashing internally for O(1) membership testing. Supports mathematical set operations like union, intersection, and difference.
hash("age") = 0
Complexity
When to use
- +Checking if an element exists in O(1)
- +Removing duplicates from a collection
- +Set operations: union, intersection, difference
- +Visited tracking in graph traversal
const set = new Set<number>([1, 2, 3, 4, 5]);
set.add(6); // O(1)
set.add(3); // O(1) - no duplicate
set.delete(1); // O(1)
const has: boolean = set.has(4); // O(1) -> true
// Set operations (ES2025)
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
const union = a.union(b); // Set {1, 2, 3, 4}
const inter = a.intersection(b); // Set {2, 3}
const diff = a.difference(b); // Set {1}
// Interview pattern: contains duplicate
const hasDup = (nums: number[]): boolean =>
new Set(nums).size !== nums.length;Linked List
manual implSequence of nodes where each node points to the next (singly linked) or both next and previous (doubly linked). Efficient insertion and deletion at any known position, but no index-based access.
traversing: 5
Complexity
When to use
- +Frequent insertion/deletion in the middle
- +Implementing LRU cache (with a hash map)
- +When you need a deque (double-ended queue)
- +Problems involving pointer manipulation
// No built-in - typed manual implementation
class ListNode<T> {
constructor(public val: T, public next: ListNode<T> | null = null) {}
}
// Build: 1 -> 2 -> 3
let head: ListNode<number> | null =
new ListNode(1, new ListNode(2, new ListNode(3)));
// Insert at head - O(1)
head = new ListNode(0, head);
// Interview pattern: reverse linked list
function reverse<T>(head: ListNode<T> | null): ListNode<T> | null {
let prev: ListNode<T> | null = null;
let curr = head;
while (curr) {
const next = curr.next; // O(1) per node
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}Sorted Set (BST)
sorted T[]Collection of unique elements maintained in sorted order, typically backed by a balanced binary search tree (red-black tree). Supports range queries and O(log n) min/max.
search(8)
Complexity
When to use
- +Maintaining a sorted collection of unique items
- +Range queries (all elements between X and Y)
- +Sliding window problems needing sorted order
- +Leaderboards, ranking systems
// No built-in sorted set - sorted array + binary search
function bisect(arr: number[], val: number): number {
let lo = 0, hi = arr.length;
while (lo < hi) {
const mid = (lo + hi) >> 1;
if (arr[mid] < val) lo = mid + 1;
else hi = mid;
}
return lo;
}
const sorted: number[] = [1, 3, 5, 8, 9];
// Insert maintaining order - O(n) for shift
const pos = bisect(sorted, 4);
sorted.splice(pos, 0, 4); // [1, 3, 4, 5, 8, 9]
const min: number = sorted[0]; // O(1)
const max: number = sorted[sorted.length - 1]; // O(1)
const has: boolean = sorted[bisect(sorted, 5)] === 5; // O(log n)Sorted Map (BST)
sorted Map<K,V>Key-value pairs maintained in sorted key order, typically backed by a balanced BST. Enables ordered iteration and range lookups that hash maps cannot provide.
search(8)
Complexity
When to use
- +You need sorted key-value pairs
- +Ordered iteration over entries
- +Range lookups by key
- +When insertion order or sorted order matters
// No built-in sorted map - Map + sorted iteration
const map = new Map<string, number>();
map.set("banana", 2);
map.set("apple", 5);
map.set("cherry", 1);
// Sort entries by key - O(n log n)
const sortedEntries: [string, number][] = [...map.entries()]
.sort(([a], [b]) => a.localeCompare(b));
for (const [key, val] of sortedEntries)
console.log(`${key}: ${val}`);
// apple: 5, banana: 2, cherry: 1
// For frequent sorted access, maintain a parallel sorted
// keys array or use a third-party balanced BST libraryPriority Queue (Heap)
manual MinHeap<T>Collection where elements are dequeued by priority rather than insertion order. Typically implemented as a binary heap. Essential for shortest-path algorithms and top-K problems.
min-heap
Complexity
When to use
- +Dijkstra's shortest path algorithm
- +Merge K sorted lists/streams
- +Top-K / Kth largest element problems
- +Event-driven simulation, scheduling
// No built-in - typed binary min-heap for interviews
class MinHeap<T> {
private data: T[] = [];
constructor(private cmp: (a: T, b: T) => number) {}
push(val: T): void { // O(log n)
this.data.push(val);
let i = this.data.length - 1;
while (i > 0) { const p = (i-1)>>1; if (this.cmp(this.data[p],this.data[i])<=0) break; [this.data[p],this.data[i]]=[this.data[i],this.data[p]]; i=p; }
}
pop(): T | undefined { // O(log n)
const top = this.data[0]; const last = this.data.pop();
if (this.data.length && last !== undefined) { this.data[0] = last; this.sinkDown(0); }
return top;
}
peek(): T | undefined { return this.data[0]; } // O(1)
get size(): number { return this.data.length; }
private sinkDown(i: number): void {
const n=this.data.length; while(true) { let s=i,l=2*i+1,r=2*i+2; if(l<n&&this.cmp(this.data[l],this.data[s])<0)s=l; if(r<n&&this.cmp(this.data[r],this.data[s])<0)s=r; if(s===i)break; [this.data[s],this.data[i]]=[this.data[i],this.data[s]]; i=s; }
}
}Concurrent Hash Map
N/AThread-safe hash map designed for concurrent read/write access from multiple threads. Uses fine-grained locking or lock-free techniques instead of a single global lock.
hash("age") = 0
Complexity
When to use
- +Multi-threaded caching
- +Shared state across threads or async tasks
- +Producer-consumer patterns with keyed data
- +When you need concurrent reads and writes
// JS/TS is single-threaded - no ConcurrentDictionary needed
// For SharedArrayBuffer workers, use Atomics:
const sab = new SharedArrayBuffer(1024);
const view = new Int32Array(sab);
// Atomic operations - thread-safe across workers
Atomics.store(view, 0, 42); // O(1) write
const val: number = Atomics.load(view, 0); // O(1) read
// Compare-and-swap (lock-free pattern)
Atomics.compareExchange(view, 0, 42, 100);
// Wait/notify (worker synchronization)
Atomics.wait(view, 0, 100); // block until changed
Atomics.notify(view, 0, 1); // wake one waiter
// In single-threaded TS, a regular Map<K, V> is safeMemory View / Slice
TypedArrayZero-copy view over a contiguous region of memory. Lets you reference a portion of an array or buffer without allocating new memory. Critical for performance-sensitive parsing and processing.
Span[0..3] = [1, 2, 3]
Complexity
When to use
- +Parsing strings or binary data without copies
- +Processing sub-arrays without allocation
- +High-performance, zero-allocation code paths
- +Interop with native or unmanaged memory
// TypedArray = contiguous memory view (like Span<T>)
const buffer = new ArrayBuffer(16);
const view = new Float64Array(buffer); // O(1) typed view
view[0] = 3.14;
view[1] = 2.72;
// Subarray - zero-copy slice, shares underlying memory
const slice: Float64Array = view.subarray(0, 1); // O(1)
slice[0] = 99.9; // mutates original buffer
// DataView for mixed types over same buffer
const dv = new DataView(buffer);
dv.setInt8(0, 127); // O(1)
const byte: number = dv.getInt8(0); // O(1) -> 127
// ArrayBuffer.transfer for resize (ES2024)
const bigger: ArrayBuffer = buffer.transfer(32); // O(n)Big O Comparison
Average-case time complexity. * = amortized.
| Structure | Access | Search | Insert | Delete |
|---|---|---|---|---|
| Array | O(1) | O(n) | O(n) | O(n) |
| Dynamic Array | O(1) | O(n) | O(1)* | O(n) |
| Stack | O(n) | O(n) | O(1)* | O(1) |
| Queue | O(n) | O(n) | O(1)* | O(1) |
| Hash Map | O(1) | O(1) | O(1)* | O(1) |
| Hash Set | N/A | O(1) | O(1)* | O(1) |
| Linked List | O(n) | O(n) | O(1) | O(1) |
| Sorted Set | O(n) | O(log n) | O(log n) | O(log n) |
| Sorted Map | O(log n) | O(log n) | O(log n) | O(log n) |
| Priority Queue | O(n) | O(n) | O(log n) | O(log n) |
| Concurrent Map | O(1) | O(1) | O(1)* | O(1) |
| Memory View | O(1) | O(n) | N/A | N/A |
Which collection should I use?
| I need to... | Use |
|---|---|
| Store items by index, resize dynamically | List / Dynamic Array |
| Map keys to values with O(1) lookup | HashMap / Dictionary |
| Track unique items, check existence in O(1) | HashSet / Set |
| Last-in-first-out (undo, DFS, brackets) | Stack |
| First-in-first-out (BFS, task queues) | Queue |
| Keep elements sorted at all times | SortedSet / TreeSet |
| Process items by priority (Dijkstra, top-K) | PriorityQueue / Heap |
| Insert/delete at a known position in O(1) | LinkedList |
| Sorted key-value pairs | SortedDictionary / TreeMap |
| Thread-safe shared cache | ConcurrentDictionary |
| Slice arrays/strings without copying | Span / Slice / memoryview |
Frequently Asked Questions
What are the most important data structures in TypeScript?add
The most commonly used are dynamic arrays (List/ArrayList/vector), hash maps (Dictionary/HashMap/dict), and hash sets. For interviews, also know stacks, queues, trees, and priority queues. These cover 90%+ of coding interview problems.
Which TypeScript data structure should I learn first?add
Start with the dynamic array and hash map. Together they solve the majority of interview problems. Then learn stacks (for DFS, bracket matching) and queues (for BFS). After that, tackle trees, heaps, and graphs.
Does Big O complexity change between languages?add
No. Big O measures algorithmic complexity, not language-specific performance. A hash map lookup is O(1) whether you use Python dict, Java HashMap, or C# Dictionary. Constant factors differ (C++ is faster than Python in wall-clock time), but Big O is the same.
Is there a built-in priority queue in TypeScript?add
It depends on the language runtime. Check the TypeScript standard library documentation for heap or priority queue support.
Know the structures.
Now use them under pressure.
Practice with an AI interviewer that asks you to implement, optimize, and explain data structure choices in real time.
Try a free mock interviewarrow_forward