To add elements to an array in TypeScript, call push() to append to the end, unshift() to add to the beginning, or splice(index, 0, value) to insert at a position in the middle. Those three change the array you already have. If you need a new array instead and want the original left alone, use spread syntax [...cities, "Boston"] or concat(). This guide shows every method with its real output, the type errors TypeScript gives you when the value does not match the array, how to add a whole array to another array, and which method is the right one for each job.
Every example was compiled and run with TypeScript 5.2.2 and Node.js v22.22.2 in the Windows Command Prompt, with tsc --target es2022 --lib es2023,dom --strict file.ts followed by node file.js, and the output below is the real output. Reference: Array.prototype.push() on MDN.
Add elements to an array in TypeScript
The three in-place methods differ only in where the new element lands:
const cities: string[] = ["Austin", "Denver", "Seattle"];
cities.push("Boston"); // add to the END
cities.unshift("Phoenix"); // add to the BEGINNING
cities.splice(2, 0, "Chicago"); // INSERT at index 2, remove nothing
console.log(cities);
console.log("length:", cities.length);
Output:
[ 'Phoenix', 'Austin', 'Chicago', 'Denver', 'Seattle', 'Boston' ]
length: 6

push() adds at the end, unshift() at the start, splice() anywhere in between.| What you want | Method | Changes the original? |
|---|---|---|
| Add at the end | arr.push(value) | Yes |
| Add at the beginning | arr.unshift(value) | Yes |
Insert at index i | arr.splice(i, 0, value) | Yes |
| Add one array to another | arr.push(...other) | Yes |
| Get a new array back | [...arr, value] or arr.concat(value) | No |
| Insert at index, new array | arr.toSpliced(i, 0, value) | No |
Add an element to the end with push()
push() is the method you will use most. It takes any number of elements, adds them in order, and returns the new length of the array, not the array itself. It works exactly the same on an array of objects, as long as each object matches the declared type:
interface Task {
id: number;
title: string;
done: boolean;
}
const sprint: Task[] = [{ id: 1, title: "Fix checkout bug", done: true }];
const newLength = sprint.push({ id: 2, title: "Update pricing page", done: false });
console.log("push() returned:", newLength); // the new length, not the array
sprint.push( // several elements in one call
{ id: 3, title: "Write release notes", done: false },
{ id: 4, title: "Deploy to staging", done: false },
);
console.log(sprint.map(t => `#${t.id} ${t.title}`));
Output:
push() returned: 2
[
'#1 Fix checkout bug',
'#2 Update pricing page',
'#3 Write release notes',
'#4 Deploy to staging'
]
If you came here looking for an append() method: TypeScript does not have one, and it does not need one. Appending means adding at the end, and that is exactly what push() does.
TypeScript checks every element you add
This is the part plain JavaScript tutorials cannot show you. The moment the value does not match the array type, the compiler refuses to build, so a bad record never reaches production:
interface Task {
id: number;
title: string;
done: boolean;
}
const sprint: Task[] = [{ id: 1, title: "Fix checkout bug", done: true }];
sprint.push({ id: 2, title: "Update pricing page" }); // forgot "done"
const prices: number[] = [19.99, 4.5];
prices.push("12"); // a string, not a number
tsc output:
push_error.ts(9,13): error TS2345: Argument of type '{ id: number; title: string; }' is not assignable to parameter of type 'Task'.
Property 'done' is missing in type '{ id: number; title: string; }' but required in type 'Task'.
push_error.ts(12,13): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

tsc before the code ever runs.That check only exists because the array has a declared element type, which is one good reason for initializing your arrays in TypeScript as const prices: number[] = []. Leave the type off and TypeScript builds it from whatever you put in: const items = [] followed by items.push("a") and items.push(1) compiles without a word and leaves you holding a (string | number)[].
Add an element to the beginning with unshift()
unshift() is the mirror image of push(): same arguments, same return value, but the elements go to the front. When you pass several, they keep the order you wrote them in:
const queue: string[] = ["order-1002", "order-1003"];
queue.unshift("order-1001"); // one element at the front
console.log(queue);
queue.unshift("order-0999", "order-1000"); // several, in the order you write them
console.log(queue);
console.log("new length:", queue.unshift("order-0998"));
Output:
[ 'order-1001', 'order-1002', 'order-1003' ]
[
'order-0999',
'order-1000',
'order-1001',
'order-1002',
'order-1003'
]
new length: 6
Insert an element at a specific index with splice()
splice() takes the start index, how many elements to delete, and then the elements to insert. Pass 0 as the delete count and nothing is removed, so it becomes a pure insert. Its return value is the list of deleted elements, which is why it is empty here:
const steps: string[] = ["Pick items", "Pack box", "Ship"];
steps.splice(2, 0, "Print label"); // at index 2, delete 0, insert 1
console.log(steps);
steps.splice(1, 0, "Weigh box", "Add invoice"); // insert several
console.log(steps);
console.log("splice() returned:", steps.splice(0, 0)); // the deleted items: none
const plan: string[] = ["Pick items", "Pack box", "Ship"];
const updated = plan.toSpliced(2, 0, "Print label"); // ES2023: a NEW array
console.log(updated);
console.log("original:", plan);
Output:
[ 'Pick items', 'Pack box', 'Print label', 'Ship' ]
[
'Pick items',
'Weigh box',
'Add invoice',
'Pack box',
'Print label',
'Ship'
]
splice() returned: []
[ 'Pick items', 'Pack box', 'Print label', 'Ship' ]
original: [ 'Pick items', 'Pack box', 'Ship' ]

splice() edits the array in place; toSpliced() returns a copy.toSpliced() is the ES2023 version that leaves the original untouched. It needs "lib": ["es2023", "dom"] (or a newer target) in your tsconfig.json, and Node.js 20 or later at runtime.
Add one array to another array
There are three ways to do this, and they are not the same. concat() and spread syntax build a new array; push() with spread adds the items to the array you already have. Pass the array itself to push() and you add it as a single nested element, which is almost never what you want. If your arrays hold objects and records with the same id should be combined rather than repeated, see merging arrays of objects in TypeScript:
const westCoast: string[] = ["Seattle", "Portland"];
const midwest: string[] = ["Chicago", "Denver"];
console.log(westCoast.concat(midwest)); // new array, both originals untouched
console.log([...westCoast, ...midwest]); // same result, spread syntax
westCoast.push(...midwest); // adds the ITEMS to westCoast
console.log(westCoast);
const regions: (string | string[])[] = ["Seattle"];
regions.push(midwest); // adds the ARRAY as one element
console.log(regions);
Output:
[ 'Seattle', 'Portland', 'Chicago', 'Denver' ]
[ 'Seattle', 'Portland', 'Chicago', 'Denver' ]
[ 'Seattle', 'Portland', 'Chicago', 'Denver' ]
[ 'Seattle', [ 'Chicago', 'Denver' ] ]

push(midwest) nests the array.Why push(…bigArray) can throw a RangeError
Spread turns every element into a separate function argument, and JavaScript engines limit how many arguments a call can take. With a few hundred elements you will never notice; with a few hundred thousand, the call blows the stack:
const big: number[] = new Array(200_000).fill(1);
const target: number[] = [];
try {
target.push(...big); // every element becomes a function argument
} catch (err) {
console.log((err as Error).name + ":", (err as Error).message);
}
for (const value of big) target.push(value); // a loop has no argument limit
console.log("loop:", target.length);
const merged: number[] = ([] as number[]).concat(big);
console.log("concat:", merged.length);
Output:
RangeError: Maximum call stack size exceeded
loop: 200000
concat: 200000
The exact cut-off depends on the engine and the stack size, so do not rely on a magic number. For large arrays, use concat() or a plain loop, both of which have no such limit.
Add to an array without changing the original
React state, Redux reducers and anything marked readonly need a new array rather than an edit in place. Spread syntax does that in both directions, and ES2023 adds with() for replacing an element by index:
interface Product {
sku: string;
price: number;
}
const cart: readonly Product[] = [{ sku: "A-100", price: 19.99 }];
const withItem: readonly Pro