The array reduce() method in TypeScript runs a callback on each element and carries one value, the accumulator, from step to step, so an array becomes a single result. Pass an initial value as the second argument; its type decides what reduce() returns:
const total = tips.reduce((acc, tip) => acc + tip, 0);
All of the output on this page is real: TypeScript 7.0.2 plus Node.js v22.22.2, strict mode, ES2024 target.
You’ll trace reduce step by step, fix the most common typing error, group and count into objects, compute several aggregates in one pass, and chain functions with it.
How the TypeScript reduce() method works
The callback receives the accumulator, the current element, its index and the whole array. Whatever you return becomes the accumulator for the next element. Logging each step makes that visible:
const tips: number[] = [4.5, 6.25, 3.75];
const total = tips.reduce((acc, tip, index) => {
const next = acc + tip;
console.log(`step ${index}: acc=${acc.toFixed(2)} + tip=${tip.toFixed(2)} -> ${next.toFixed(2)}`);
return next; // becomes acc on the next step
}, 0);
console.log("total tips:", total.toFixed(2));
Output:
step 0: acc=0.00 + tip=4.50 -> 4.50
step 1: acc=4.50 + tip=6.25 -> 10.75
step 2: acc=10.75 + tip=3.75 -> 14.50
total tips: 14.50

Plain totals are the classic use, and my guide to summing an array in TypeScript covers them, including the TypeError you get on an empty array with no initial value.
Why does reduce with {} give an error in TypeScript?
When the seed is a bare {}, TypeScript infers the accumulator as the empty object type, so you can’t add keys to it:
interface Order { id: number; state: string; amount: number }
const orders: Order[] = [
{ id: 1, state: "TX", amount: 120.0 },
{ id: 2, state: "CA", amount: 89.99 },
{ id: 3, state: "TX", amount: 45.5 },
];
const perState = orders.reduce((acc, o) => {
acc[o.state] = (acc[o.state] ?? 0) + o.amount;
return acc;
}, {});
Output:
reduce_seed_error.ts(10,3): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
No index signature with a parameter of type 'string' was found on type '{}'.
reduce_seed_error.ts(10,19): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
No index signature with a parameter of type 'string' was found on type '{}'.

Tell TypeScript what the accumulator will become. Either pass a generic to reduce or type the seed. Both compile and give the same kind of result:
interface Order { id: number; state: string; amount: number }
const orders: Order[] = [
{ id: 1, state: "TX", amount: 120.0 },
{ id: 2, state: "CA", amount: 89.99 },
{ id: 3, state: "TX", amount: 45.5 },
{ id: 4, state: "NY", amount: 230.25 },
];
// Fix 1: pass the accumulator type as a generic
const perState = orders.reduce<Record<string, number>>((acc, o) => {
acc[o.state] = (acc[o.state] ?? 0) + o.amount;
return acc;
}, {});
console.log(perState);
// Fix 2: type the seed itself
const countByState = orders.reduce((acc, o) => {
acc[o.state] = (acc[o.state] ?? 0) + 1;
return acc;
}, {} as Record<string, number>);
console.log(countByState);
Output:
{ TX: 165.5, CA: 89.99, NY: 230.25 }
{ TX: 2, CA: 1, NY: 1 }
I prefer the generic form, reduce<Record<string, number>>, because the type sits next to the method name. If Record is new to you, read TypeScript Record explained.
Group an array with reduce() or Object.groupBy()
Grouping is reduce’s other everyday job. Since ES2024 there’s also a built-in Object.groupBy(), which needs --target es2024 or later:
interface Order { id: number; state: string; amount: number }
const orders: Order[] = [
{ id: 1, state: "TX", amount: 120.0 },
{ id: 2, state: "CA", amount: 89.99 },
{ id: 3, state: "TX", amount: 45.5 },
{ id: 4, state: "NY", amount: 230.25 },
];
const byState = orders.reduce<Record<string, number[]>>((acc, o) => {
(acc[o.state] ??= []).push(o.id);
return acc;
}, {});
console.log("reduce: ", byState);
const grouped = Object.groupBy(orders, (o) => o.state); // ES2024
console.log("groupBy TX: ", grouped.TX?.map((o) => o.id));
Output:
reduce: { TX: [ 1, 3 ], CA: [ 2 ], NY: [ 4 ] }
groupBy TX: [ 1, 3 ]

??= creates the array the first time a state appears. Use groupBy when you want whole objects, and reduce when you want something custom, like just the IDs. For key-to-value lookups, creating a Map from an array is often cleaner.
Calculate count, sum, average, min and max in one pass
Like SQL aggregate functions, an object accumulator can track several results at once, so you walk the array a single time:
const sales: number[] = [1250.5, 980.25, 1120.75, 640.0, 1499.99];
interface Stats { count: number; sum: number; min: number; max: number }
const s = sales.reduce<Stats>(
(acc, v) => ({
count: acc.count + 1,
sum: acc.sum + v,
min: Math.min(acc.min, v),
max: Math.max(acc.max, v),
}),
{ count: 0, sum: 0, min: Infinity, max: -Infinity },
);
console.log(`COUNT ${s.count}`);
console.log(`SUM $${s.sum.toFixed(2)}`);
console.log(`AVG $${(s.sum / s.count).toFixed(2)}`);
console.log(`MIN $${s.min.toFixed(2)} MAX $${s.max.toFixed(2)}`);
Output:
COUNT 5
SUM $5491.49
AVG $1098.30
MIN $640.00 MAX $1499.99

Seed min with Infinity and max with -Infinity so the first value always wins. If you need the whole object with the highest value, see finding the maximum in an array of objects.
Chain functions with reduce() and reduceRight()
The accumulator doesn’t have to be a number or an object. Here it’s a price passed through a list of functions, and reduceRight() walks the array from the end:
type Step = (price: number) => number;
const addTax: Step = (p) => p * 1.0825;
const discount: Step = (p) => p * 0.9;
const round: Step = (p) => Math.round(p * 100) / 100;
const steps: Step[] = [discount, addTax, round];
const price = steps.reduce((value, fn) => fn(value), 100);
console.log("left to right: ", price);
const word = ["T", "S", "!"].reduceRight((acc, ch) => acc + ch, "");
console.log("reduceRight: ", word);
Output:
left to right: 97.43
reduceRight: !ST
The pipeline applies the discount, then tax, then rounding. reduce can also flatten nested arrays, though flat() is clearer; see flattening an array of arrays in TypeScript.
When to use reduce() and when not to
| Task | Better choice |
|---|---|
| Total of numbers | reduce((a, b) => a + b, 0) |
| Group whole objects | Object.groupBy() |
| Transform each item | map() |
| Keep some items | filter() |
| Several aggregates at once | reduce() with an object accumulator |
| Run side effects | for...of or forEach() |
If a reduce callback needs a comment to explain it, a loop is usually easier for the next reader. My go-to there is covered in for…of loops in TypeScript.
Next, try these other array methods:
- Filter arrays in TypeScript
- Use the forEach method on TypeScript arrays
- Get distinct values from an array
- Merge arrays of objects in TypeScript
Frequently asked questions
What does the reduce method do in TypeScript?
It calls a callback for every element and passes the result along as an accumulator, returning one final value.
How do I type the accumulator in reduce?
Pass a generic, as in arr.reduce<Record<string, number>>(fn, {}), or type the seed with {} as Record<string, number>.
Why do I get an error when using {} as the initial value?
TypeScript infers the accumulator as an empty object type with no index signature. Give the seed a type.
Should I use reduce or Object.groupBy?
Use Object.groupBy() for plain grouping on ES2024+. Use reduce when you need a custom shape.
What is the difference between reduce and reduceRight?
reduceRight processes the array from the last element to the first.
Where is Array.prototype.reduce documented?
See Array.prototype.reduce() on MDN.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn