You map an array of objects to another array in TypeScript with Array.prototype.map(). Give the callback a typed input and an explicit return type, and wrap the object literal in parentheses:
const cards: EmployeeCard[] = employees.map((e: Employee): EmployeeCard => ({
id: e.id,
name: `${e.firstName} ${e.lastName}`,
}));
I compiled every example below in strict mode with TypeScript 7.0.2 and Node.js v22.22.2, and each output block is copied from my console.
Map an array of objects to a new interface
Here, each Employee in Austin or Denver becomes a lighter EmployeeCard. If you’re new to typed object arrays, start with arrays of objects in TypeScript.
interface Employee {
id: number;
firstName: string;
lastName: string;
city: string;
salary: number;
}
const employees: Employee[] = [
{ id: 101, firstName: "Emma", lastName: "Carter", city: "Austin", salary: 98500 },
{ id: 102, firstName: "Liam", lastName: "Brooks", city: "Denver", salary: 87250 },
{ id: 103, firstName: "Olivia", lastName: "Hayes", city: "Austin", salary: 112000 },
];
interface EmployeeCard {
id: number;
name: string;
}
const cards: EmployeeCard[] = employees.map((e: Employee): EmployeeCard => ({
id: e.id,
name: `${e.firstName} ${e.lastName}`,
}));
console.log(cards);
Output:
[
{ id: 101, name: 'Emma Carter' },
{ id: 102, name: 'Liam Brooks' },
{ id: 103, name: 'Olivia Hayes' }
]

map() returns a new array with the same length; the original employees keep every field.The explicit : EmployeeCard return type matters. Without it, a typo only shows up later, wherever the array gets used.
Why does map() return undefined for my objects?
In an arrow function, braces start a block, not an object. So { id: e.id } is a label plus an expression, and you get nothing back:
// Employee interface and employees array from the first example
// Braces without parentheses = a function body with a label, not an object
const broken = employees.map((e) => { id: e.id });
const fixed = employees.map((e) => ({ id: e.id }));
console.log("broken:", broken);
console.log("fixed: ", fixed);
Output:
broken: [ undefined, undefined, undefined ]
fixed: [ { id: 101 }, { id: 102 }, { id: 103 } ]
Parentheses make it an expression again. For loops that don’t need a return value, the forEach() method is the better fit.
TypeScript map() type errors caught by tsc
Here’s where TypeScript earns its keep. The first callback forgets city; the second leaks salary into a type that doesn’t allow it:
interface Employee { id: number; firstName: string; city: string; salary: number }
interface EmployeeCard { id: number; name: string; city: string }
const employees: Employee[] = [{ id: 101, firstName: "Emma", city: "Austin", salary: 98500 }];
const missing = employees.map((e): EmployeeCard => ({ id: e.id, name: e.firstName }));
const extra = employees.map((e): EmployeeCard => ({
id: e.id,
name: e.firstName,
city: e.city,
salary: e.salary,
}));
Output:
map_errors.ts(6,53): error TS2741: Property 'city' is missing in type '{ id: number; name: string; }' but required in type 'EmployeeCard'.
map_errors.ts(12,3): error TS2353: Object literal may only specify known properties, and 'salary' does not exist in type 'EmployeeCard'.

city; TS2353 flags the extra salary property.Excess-property checks only apply to fresh object literals, which is what a map() callback returns. Interfaces for object arrays are covered in TypeScript interface for an array of objects.
Add, rename or omit object fields in map()
Destructuring in the callback keeps the mapping short. Rest spread drops a field, and renaming happens inline:
// Employee interface and employees array from the first example
type PublicEmployee = Omit<Employee, "salary">;
type Directory = Pick<Employee, "id" | "city"> & { fullName: string };
// Omit a field with rest spread
const publicList: PublicEmployee[] = employees.map(({ salary, ...rest }) => rest);
// Rename and combine fields while destructuring
const directory: Directory[] = employees.map(({ id, firstName: first, lastName: last, city }) => ({
id,
city,
fullName: `${last}, ${first}`,
}));
console.log(publicList[0]);
console.log(directory[1]);
Output:
{ id: 101, firstName: 'Emma', lastName: 'Carter', city: 'Austin' }
{ id: 102, city: 'Denver', fullName: 'Brooks, Liam' }
Omit and Pick save you retyping fields. To add a field, I spread: ({ ...e, bonusUSD: (e.salary * 0.05).toFixed(2) }).
Spread copies are shallow, so nested objects are still shared. The TypeScript spread operator guide shows where that bites.
Use the index while mapping objects
The callback also receives the index and the whole array. Here I number US orders and format totals as dollars:
interface Order { orderId: string; total: number }
const orders: Order[] = [{ orderId: "A-5001", total: 249.5 }, { orderId: "A-5002", total: 1180 }];
const usd = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
console.log(orders.map((o, i) => ({ line: i + 1, ...o, total: usd.format(o.total) })));
Output:
[
{ line: 1, orderId: 'A-5001', total: '$249.50' },
{ line: 2, orderId: 'A-5002', total: '$1,180.00' }
]
If you need one grand total rather than a new array, reach for the reduce() method in TypeScript.
Combine map with flatMap and filter
When one object should produce several rows, you want flatMap(). It maps, then flattens one level:
interface LineItem { sku: string; qty: number; price: number }
interface Order { orderId: string; status: "paid" | "refunded"; items: LineItem[] }
const orders: Order[] = [
{ orderId: "A-5001", status: "paid", items: [{ sku: "MUG-12", qty: 2, price: 14.99 }] },
{ orderId: "A-5002", status: "refunded", items: [{ sku: "BOOK-TS", qty: 1, price: 39.99 }] },
{ orderId: "A-5003", status: "paid", items: [{ sku: "MAT-YOGA", qty: 3, price: 25 }] },
];
// One output row per line item
const lines = orders.flatMap((o) =>
o.items.map((it) => ({ orderId: o.orderId, sku: it.sku, amount: (it.qty * it.price).toFixed(2) }))
);
console.log(lines);
// Filter first, then map: only paid orders
const paidTotals = orders
.filter((o) => o.status === "paid")
.map((o) => ({ orderId: o.orderId, total: o.items.reduce((s, it) => s + it.qty * it.price, 0).toFixed(2) }));
console.log(paidTotals);
Output:
[
{ orderId: 'A-5001', sku: 'MUG-12', amount: '29.98' },
{ orderId: 'A-5002', sku: 'BOOK-TS', amount: '39.99' },
{ orderId: 'A-5003', sku: 'MAT-YOGA', amount: '75.00' }
]
[
{ orderId: 'A-5001', total: '29.98' },
{ orderId: 'A-5003', total: '75.00' }
]

flatMap() flattens the line items, and filter().map() keeps only paid orders.Filtering first keeps your map callback simple. For more patterns, see filtering an array of objects in TypeScript and flattening an array of arrays.
Map an array of objects with async functions
An async callback makes map() return promises. Await them with Promise.all() and you get results in the original order:
interface Store { id: number; city: string }
interface StoreSales { city: string; salesUSD: string }
const stores: Store[] = [
{ id: 1, city: "Austin" },
{ id: 2, city: "Denver" },
{ id: 3, city: "Chicago" },
];
const fetchSales = (id: number): Promise<number> =>
new Promise((resolve) => setTimeout(() => resolve(id * 1520.4), 100 * (4 - id)));
async function main(): Promise<void> {
const pending = stores.map(async (s) => ({ city: s.city, salesUSD: (await fetchSales(s.id)).toFixed(2) }));
console.log("without await:", pending[0] instanceof Promise);
const report: StoreSales[] = await Promise.all(pending);
console.log(report);
}
main();
Output:
without await: true
[
{ city: 'Austin', salesUSD: '1520.40' },
{ city: 'Denver', salesUSD: '3040.80' },
{ city: 'Chicago', salesUSD: '4561.20' }
]

Promise.all() keeps Austin, Denver, Chicago in order.You’ll find these array tutorials useful next:
- Sort an array of objects by property value
- Find an object in a TypeScript array
- Merge arrays of objects in TypeScript
- Compare strings in TypeScript
Frequently asked questions
How do I map an array of objects to another array in TypeScript?
Call map() with a typed callback: (e: Employee): EmployeeCard => ({ ... }).
Does map() change the original array?
No. It returns a new array of the same length, as long as your callback returns new objects instead of mutating them.
How do I remove a property while mapping?
Use rest spread, ({ salary, ...rest }) => rest, typed as Omit<Employee, "salary">.
How do I use await inside map()?
Make the callback async and wrap the call in await Promise.all(items.map(async (x) => ...)).
Where is map() documented?
See Array.prototype.map() 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