Arrays & Tuples
When one value isn't enough
You've been working with single values — one string, one number, one boolean. That's fine for simple scenarios, but real code needs collections — groups of values bundled together. TypeScript gives you two powerful ways to handle collections: Arrays and Tuples.
Arrays are flexible, same-type collections. Think of them like the unlimited rice bowl at a Hyderabadi thali — you can keep adding more rice, but it's all rice. Every element is the same type. You can have 3 items, 30 items, or 300 — the length isn't fixed, but the type is consistent throughout.
Tuples are fixed-size, fixed-type-per-position collections. Think of the "Special Thali" at your favorite Irani cafe — exactly 1 dal, 1 sabzi, 2 rotis, 1 biryani, 1 dessert. Each position has a specific item, a specific type, and a specific count. You can't put dessert where the dal goes, and you can't add a 7th item to a 6-item fixed menu.
The key difference comes down to flexibility vs. structure:
- Arrays: flexible length, same type → use for lists of similar items
- Tuples: fixed length, specific type per position → use for small, structured, positional data
Understanding when to use which is critical. Most of the time, you'll use arrays. Tuples are for specific scenarios like key-value pairs, coordinate points, or React's useState return values. Let's dig into the syntax and see how each one works in practice.
In TypeScript, there are two ways to declare an array type. The preferred syntax uses square brackets after the type: number[]. This reads naturally — "an array of numbers." The generic syntax uses Array<number> — it means the same thing but is less common in everyday code.
// Preferred syntax — use this
const scores: number[] = [95, 87, 72];
// Generic syntax — same thing, less common
const scores: Array<number> = [95, 87, 72];
Both are equivalent. Stick with type[] unless you're working with complex generic types where Array<T> reads better.
Arrays of each primitive:
const names: string[] = ["Hyderabad", "Secunderabad", "Cyberabad"];
const prices: number[] = [250, 450, 120];
const flags: boolean[] = [true, false, true];
Mixed content with union types: When you need an array that can hold multiple types, use a union inside parentheses:
const mixed: (string | number)[] = ["Biryani", 250, "Chai", 30];
Notice the parentheses! (string | number)[] means "an array of strings or numbers." Without parentheses, string | number[] means "a string OR an array of numbers" — completely different! This is a common source of bugs.
Nested arrays (2D arrays):
const matrix: number[][] = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
Initialization:
const nums: number[] = [1, 2, 3]; // with values
const empty: string[] = []; // empty, but typed
Typed array methods: One of TypeScript's biggest benefits — array methods are automatically typed. If you have a number[], you can't push a string:
const prices: number[] = [100, 200];
prices.push(300); // ✅ OK
prices.push("free"); // ❌ Error! Argument of type 'string' is not assignable to 'number'
Methods like map, filter, reduce all know the element type and give you proper autocomplete and type checking. This is where TypeScript really shines for day-to-day coding.
The readonly modifier: When you want an array that can't be modified:
const days: readonly string[] = ["Mon", "Tue", "Wed"];
days.push("Thu"); // ❌ Error! Property 'push' does not exist
days[0] = "Sunday"; // ❌ Error! Index signature only permits reading
Use readonly when you want to guarantee the array won't be mutated — like configuration data, constants, or data passed between components that should remain untouched.
In TypeScript, a tuple is an array with a fixed number of elements where each position has a specific type. Unlike regular arrays where every element shares the same type, tuples let you define exactly what type goes at each index.
Basic tuple syntax:
const user: [string, number] = ["Hyderabad", 6800000];
// position 0 = string, position 1 = number
This means: position 0 must be a string, position 1 must be a number. Exactly 2 elements. No more, no less. The order is strict — you can't swap them.
Destructuring tuples: Since tuples have a fixed structure, destructuring works perfectly:
const landmark: [string, number] = ["Charminar", 1591];
const [name, year] = landmark;
// name: string, year: number — TypeScript infers both types correctly
console.log(name); // "Charminar"
console.log(year); // 1591
TypeScript knows the type of each destructured variable based on its position in the tuple. This makes destructuring tuples feel natural and type-safe.
Accessing by index:
const rgb: [number, number, number] = [255, 128, 0];
const red = rgb[0]; // TypeScript knows: number
const green = rgb[1]; // TypeScript knows: number
const blue = rgb[2]; // TypeScript knows: number
Each index access gives you the exact type for that position. This is more precise than a regular number[] where every index just gives you number without any positional meaning.
Order matters! This is the most critical thing about tuples:
const good: [string, number] = ["hello", 42]; // ✅ Correct
const bad: [string, number] = [42, "hello"]; // ❌ Error!
// Type 'number' is not assignable to type 'string'
Position 0 = string. Position 1 = number. No swapping, no flexibility. If the types don't match their positions, TypeScript will catch it immediately.
Common tuple use cases:
- Key-value pairs:
[string, number]— like["population", 6800000] - RGB colors:
[number, number, number]— like[255, 128, 0] - Coordinates:
[number, number]— like[17.3850, 78.4867] - React useState:
[T, (val: T) => void]— like[string, Dispatch<SetStateAction<string>>]
Optional elements in tuples:
const entry: [string, number?] = ["Hyderabad"];
// Second element is optional — can be present or absent
Rest elements in tuples:
const scores: [string, ...number[]] = ["Hyderabad", 95, 87, 72];
// First element is string, remaining elements are all numbers
Tuples are stricter than arrays. They're for when you know exactly how many elements you have and what type each position holds. Use them for small, obvious, positional data — not for arbitrary lists of unknown length.
Tuples have some sneaky behaviors that catch even experienced developers off guard. Let's walk through the three most common traps so you don't fall into them.
Trap #1: Tuples secretly allow .push()
const tup: [string, number] = ["hello", 42];
tup.push(true); // Compiles without error in many TS versions!
Wait — we said the tuple is [string, number], so how can we push a boolean? This happens because tuples extend Array internally in TypeScript. The type system checks the length and types at assignment time but not at mutation time. So .push(), .pop(), .shift() can all bypass the length constraint.
The fix: Use readonly tuples to prevent mutation entirely:
const tup: readonly [string, number] = ["hello", 42];
tup.push(true); // ❌ Error! Property 'push' does not exist on 'readonly [string, number]'
tup[0] = "hi"; // ❌ Error! Cannot assign to '0' because it is read-only
This is the safest way to work with tuples. If you don't need to mutate, make it readonly.
Trap #2: Confusing array-of-tuples syntax
// Array of tuples — each element is a [string, number] tuple
const pairs: [string, number][] = [["a", 1], ["b", 2]];
// This is NOT the same as:
const wrong: [string, number[]] = ["a", [1, 2, 3]];
// ↑ A tuple: string at position 0, number[] at position 1
// And this is COMPLETELY different:
const oops: string | number[] = "hello";
// ↑ Either a string OR an array of numbers — no tuple at all!
Operator precedence matters! [string, number][] means an array of [string, number] tuples. string | number[] means string or number[] — a union, not a tuple. Always use parentheses and brackets carefully, and double-check complex type expressions.
Trap #3: Using tuples for data that should be an object
// ❌ Bad — what does position 2 mean? Nobody knows!
const city: [string, number, boolean] = ["Hyderabad", 6800000, true];
// ✅ Good — each field has a name, meaning is clear
const city: { name: string; population: number; isCapital: boolean } = {
name: "Hyderabad",
population: 6800000,
isCapital: true,
};
When you read city[2] in code, you have no idea what true means. Is it isCapital? isMetro? hasAirport? With an object, city.isCapital is self-documenting. Tuples are for small, obvious, positional data — like [key, value] pairs, [x, y] coordinates, or [r, g, b] colors. If you need more than 3-4 elements, or if the positional meaning isn't immediately obvious, use an object instead.
Here's your complete cheatsheet for array and tuple types in TypeScript. Bookmark this page — you'll come back to it often.
Array Types:
// Preferred syntax
const a: number[] = [1, 2, 3];
// Generic syntax (equivalent)
const b: Array<number> = [1, 2, 3];
// Union array — each element can be string or number
const c: (string | number)[] = ["hello", 42];
// Multi-dimensional array
const d: number[][] = [[1, 2], [3, 4]];
// Array of tuples
const e: [string, number][] = [["a", 1], ["b", 2]];
// Readonly array — can't modify
const f: readonly string[] = ["Mon", "Tue"];
Tuple Types:
// Basic tuple — fixed length, fixed type per position
const a: [string, number] = ["Hyderabad", 6800000];
// Optional element (second position may be absent)
const b: [string, number?] = ["Hyderabad"];
// Rest element — first is string, rest are all numbers
const c: [string, ...number[]] = ["Scores", 95, 87, 72];
// Readonly tuple — can't mutate
const d: readonly [string, number] = ["key", 42];
// Named tuples for better readability (TS 4.0+)
const e: [name: string, age: number] = ["Charminar", 1591];
Quick Comparison:
- Arrays = flexible length, same type → use for lists
- Tuples = fixed length, specific type per position → use for small structured data
- Objects = named fields, any length → use for complex structured data
The Golden Rule:
"If it's a list of similar items, use an array.
If it's a fixed structure with position meaning, use a tuple.
If it's more than 3–4 elements, use an object instead."
Common mistakes to avoid:
string | number[]is NOT(string | number)[]— parentheses matter[string, number][]is an array of tuples, not a tuple containing an array- Tuples allow
.push()— usereadonlyto prevent mutation - Don't use tuples for data with unclear positional meaning — objects are more readable
Key Points
- Arrays use `type[]` syntax — flexible length, same type throughout
- Tuples use `[type1, type2]` syntax — fixed length, specific type per position
- Use `readonly` to prevent mutations on arrays and tuples
- (string | number)[] ≠ string | number[] — parentheses change the meaning!
- Tuples are for small, positional data — use objects for complex structures
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login