A TypeScript 2D array is an array whose elements are arrays. For numbers, write number[][]; then use nested loops to visit each row and cell. Use an interface when it gives a meaningful name to a row or to structured cell objects—not just to restate basic array syntax.
How do you create a 2D array in TypeScript?
Declare an array of arrays and give it the element type you need. This example creates a grid of numbers and logs each value using its row and column indexes:
As an Amazon Associate I earn from qualifying purchases.
const grid: number[][] = [
[1, 2, 3],
[4, 5, 6],
];
for (let row = 0; row < grid.length; row++) {
for (let column = 0; column < grid[row].length; column++) {
console.log(grid[row][column]);
}
}
The outer array holds rows, and each row is itself an array of numbers. TypeScript’s handbook documents T[] array notation and the equivalent generic form Array<T>, so number[][] is equivalent to Array<Array<number>>. See the TypeScript Handbook: Object Types and Everyday Types.
How do you loop through rows and values?
Use indexed loops when you need coordinates
The nested for loops above expose both indexes: row identifies the current row, and column identifies the position within it. Each inner loop uses that row’s own length, which also works when rows have different lengths.
#1 Best Overall
Use for...of when indexes are unnecessary
for (const row of grid) {
for (const value of row) {
console.log(value);
}
}
This version visits each value directly without maintaining index variables. Choose indexed loops for coordinate-based work, such as updating a particular cell, and value iteration when you only need to process the contents.
What does number[][] guarantee?
It says that the outer array contains arrays of numbers. It does not guarantee that every row has the same length, so validate dimensions separately if your application requires a rectangular grid. For example, a row may contain fewer or more elements than another while still fitting the type.
Rank #2
- TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
- TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
For values other than numbers, replace the element type: string[][] is an array of string arrays. The shorthand T[][] is concise for simple types; the generic spelling Array<Array<T>> can make deeply nested or complex types easier to read. Google’s TypeScript Style Guide recommends the concise nested-array form for simple types and notes that the generic form can help with more complex nested types.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
When should you use an interface?
Name a row contract when that helps communicate intent
A numeric index signature describes values retrieved using numeric indexes, making it possible to name a row type:
interface NumberRow {
[index: number]: number;
}
const grid: NumberRow[] = [
[1, 2, 3],
[4, 5, 6],
];
This can be useful when the row contract has a domain-specific meaning or is reused. For a basic numeric grid, number[][] is simpler. The handbook’s Object Types section explains that a numeric index signature describes the value returned when an object is indexed by a number.
Describe structured cells with an interface
When each cell contains named fields, define the cell shape once and nest arrays of that interface:
interface Cell {
value: number;
label: string;
}
const cells: Cell[][] = [
[{ value: 1, label: "A1" }, { value: 2, label: "A2" }],
[{ value: 3, label: "B1" }, { value: 4, label: "B2" }],
];
Here Cell explains what each position represents, while Cell[][] expresses the two levels of arrays. The TypeScript team’s official “Walkthrough: Interfaces” states: “The only job of an interface in TypeScript is to describe a type.” An interface describes the shape; it does not create an array or check arbitrary external data at runtime. Type information is erased during compilation.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteShould you use arrays, tuples, or readonly types?
| Need | Type | What it expresses |
|---|---|---|
| Rows whose lengths can vary | number[][] |
An array of number arrays; row lengths need not match. |
| Known, fixed positions and length | [string, number] |
A tuple with a string at position 0 and a number at position 1. |
| Prevent changes to the outer array through this reference | readonly number[][] |
The outer array is readonly, but nested rows remain mutable through this type. |
| Prevent changes to both outer array and rows through this reference | readonly (readonly number[])[] |
Both array levels are readonly through this type. |
The tuple type [number] is not a 2D number array: it describes a tuple with one number position. For fixed-length rows with known positions, use tuple types; ordinary nested arrays are the flexible choice. The handbook distinguishes tuple types from regular arrays in Everyday Types and documents readonly array types in Object Types.
Best Value
Readonly types restrict mutation through the typed reference; they do not freeze the underlying JavaScript data at runtime. Choose the readonly form that matches the level your code should treat as immutable.
How do you build rows procedurally without sharing them?
Create a fresh inner array for each row. If multiple outer-array positions point to the same row array, changing one row’s contents will also appear through the other positions because both refer to the same JavaScript object.
const rows: number[][] = [];
for (let row = 0; row < 2; row++) {
const values: number[] = [];
for (let column = 0; column < 3; column++) {
values.push(0);
}
rows.push(values);
}
Each pass through the outer loop creates a new values array, so the rows are distinct.
Recommended Free Tools
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




