How to Use the Array reduce() Method in TypeScript

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
Command Prompt output tracing TypeScript array reduce, showing the accumulator and current value at each step
Each step prints the accumulator going in, the tip being added and the new value, ending with the total.

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 '{}'.
Command Prompt showing TypeScript error TS7053 when reduce uses an empty object as the initial value
The compiler rejects acc[o.state] because the accumulator was inferred as an empty object 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 ]
Command Prompt output of TypeScript grouping orders by state with reduce and with Object.groupBy
reduce collects the order IDs per state; Object.groupBy gives the same TX orders, 1 and 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
Command Prompt output of TypeScript reduce computing count, sum, average, minimum and maximum sales in one pass
The five sales give COUNT, SUM, AVG, MIN and MAX lines, all formatted in dollars.

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

TaskBetter choice
Total of numbersreduce((a, b) => a + b, 0)
Group whole objectsObject.groupBy()
Transform each itemmap()
Keep some itemsfilter()
Several aggregates at oncereduce() with an object accumulator
Run side effectsfor...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:

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?