The quickest way to convert an array to a dictionary in TypeScript is Object.fromEntries(): map each item to a [key, value] pair and type the result as a Record. Here’s the one-liner I use for an array of objects keyed by id:
const byId: Record<string, Employee> = Object.fromEntries(
employees.map((e) => [e.id, e]),
);
Every snippet below ran in strict mode on TypeScript 7.0.2 and Node.js v22.22.2, and each output block is pasted straight from my console.
Convert an array of objects to a dictionary with Object.fromEntries()
Say you look up Austin and Denver employees by id all day. Calling find() scans the whole array each time; a dictionary gives you a direct lookup. If typed object arrays are new to you, start with arrays of objects in TypeScript.
interface Employee {
id: string;
name: string;
city: string;
salaryUSD: number;
}
const employees: Employee[] = [
{ id: "E101", name: "Ava Thompson", city: "Austin", salaryUSD: 98500 },
{ id: "E102", name: "Liam Carter", city: "Denver", salaryUSD: 87250.5 },
{ id: "E103", name: "Mia Rodriguez", city: "Austin", salaryUSD: 104000 },
];
const byId: Record<string, Employee> = Object.fromEntries(
employees.map((e) => [e.id, e]),
);
console.log(byId["E102"].name); // direct lookup, no find()
console.log(Object.keys(byId));
Output:
Liam Carter
[ 'E101', 'E102', 'E103' ]

byId["E102"] instantly.The value can be any field you put in the pair. It’s the same idea as mapping an array of objects in TypeScript.
Build the dictionary with reduce()
Many codebases use reduce() instead, but in strict mode an untyped {} seed won’t compile:
// Employee interface and employees array from the first example
const byId = employees.reduce((acc, e) => {
acc[e.id] = e;
return acc;
}, {});
Output:
dict_reduce_err.ts(15,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 '{}'.

{} with a string.The fix is to pass the result type as reduce’s generic argument. I mutate the accumulator, because spreading {...acc} copies it every time:
// Employee interface and employees array from the first example
const byId = employees.reduce<Record<string, Employee>>((acc, e) => {
acc[e.id] = e; // mutate the accumulator, no spread copy
return acc;
}, {});
console.log(byId["E103"].city, byId["E103"].salaryUSD.toFixed(2));
Output:
Austin 104000.00
Everything else reduce() can do is in my guide to the array reduce method in TypeScript.
Convert an array to a dictionary with a for…of loop or forEach
A plain loop is easy to debug, and it handles string arrays too, like counting orders per state:
// Employee interface and employees array from the first example
const nameById: Record<string, string> = {};
for (const e of employees) {
nameById[e.id] = e.name;
}
console.log(nameById);
const states: string[] = ["TX", "CA", "TX", "NY", "TX", "CA"];
const ordersByState: Record<string, number> = {};
states.forEach((s) => {
ordersByState[s] = (ordersByState[s] ?? 0) + 1;
});
console.log(ordersByState);
Output:
{ E101: 'Ava Thompson', E102: 'Liam Carter', E103: 'Mia Rodriguez' }
{ TX: 3, CA: 2, NY: 1 }
The ?? 0 starts each new key at zero. For a comparison of loop styles, see how to loop through an array in TypeScript and the forEach() method.
Write a reusable toDictionary() helper
If you convert arrays often, a generic helper keeps the types right. Pass a key picker and, optionally, a value picker:
// Employee interface and employees array from the first example
function toDictionary<T, K extends PropertyKey, V = T>(
items: readonly T[],
getKey: (item: T) => K,
getValue: (item: T) => V = (item) => item as unknown as V,
): Record<K, V> {
const result = {} as Record<K, V>;
for (const item of items) {
result[getKey(item)] = getValue(item);
}
return result;
}
const byId = toDictionary(employees, (e) => e.id);
const payById = toDictionary(employees, (e) => e.id, (e) => e.salaryUSD.toLocaleString("en-US", { style: "currency", currency: "USD" }));
console.log(byId["E101"].name, "|", payById);
const lookup: Record<string, Employee | undefined> = byId;
const missing = lookup["E999"];
console.log("E999:", missing?.name ?? "not found", "| hasOwn:", Object.hasOwn(byId, "E999"));
Output:
Ava Thompson | { E101: '$98,500.00', E102: '$87,250.50', E103: '$104,000.00' }
E999: not found | hasOwn: false

E999 key comes back as not found.Watch that last line. A Record<string, Employee> claims every key exists, so byId["E999"].name compiles yet crashes at runtime. Widen lookups to Employee | undefined, check Object.hasOwn(), or enable noUncheckedIndexedAccess.
What happens to duplicate keys in a dictionary?
Keying by city drops one of the two Austin employees. Every method in this article keeps the last value. When you need all of them, use Object.groupBy() (ES2024):
// Employee interface and employees array from the first example
const byCity = Object.fromEntries(employees.map((e) => [e.city, e.name]));
console.log("last one wins:", byCity);
const groups = Object.groupBy(employees, (e) => e.city);
for (const [city, people] of Object.entries(groups)) {
console.log(city, "->", people?.map((p) => p.name).join(", "));
}
Output:
last one wins: { Austin: 'Mia Rodriguez', Denver: 'Liam Carter' }
Austin -> Ava Thompson, Mia Rodriguez
Denver -> Liam Carter

Object.groupBy() keeps both under Austin.Should you use a Map instead of an object?
A Map is TypeScript’s other dictionary. It keeps key types and has size and has() built in:
// Employee interface and employees array from the first example
const byId = new Map(employees.map((e) => [e.id, e]));
console.log(byId.get("E101")?.name, "| size:", byId.size, "| has E999:", byId.has("E999"));
const zipCodes = [78701, 80202, 60601];
const asObject = Object.fromEntries(zipCodes.map((z) => [z, true]));
const asMap = new Map(zipCodes.map((z) => [z, true]));
console.log("object keys:", Object.keys(asObject));
console.log("map keys: ", [...asMap.keys()]);
Output:
Ava Thompson | size: 3 | has E999: false
object keys: [ '60601', '78701', '80202' ]
map keys: [ 78701, 80202, 60601 ]
Notice the object turned the ZIP codes into strings and sorted them, while the Map kept numbers in insertion order. I go deeper in creating a Map from an array in TypeScript.
| Approach | Result type | Best for |
|---|---|---|
Object.fromEntries() | Record<string, T> | Short, readable one-liners |
reduce<Record<...>>() | Record<string, T> | Extra logic per item |
Object.groupBy() | Partial<Record<K, T[]>> | Keys that repeat |
new Map() | Map<K, T> | Non-string keys, frequent updates |
Here are a few related TypeScript guides to read next:
- Work with key-value pairs in TypeScript
- Merge Maps in TypeScript
- Find an object in a TypeScript array
- TypeScript interface for an array of objects
Frequently asked questions
How do I convert an array to a dictionary in TypeScript?
Use Object.fromEntries(arr.map((x) => [x.id, x])) and annotate the result as Record<string, T>.
Why does reduce() give error TS7053 when building a dictionary?
The {} seed has type {}, which has no string index. Write reduce<Record<string, T>>(..., {}).
What happens if two items have the same key?
The later item overwrites the earlier one. Use Object.groupBy() to keep every item per key.
Should I use Record or Map for a dictionary?
Use a Record for JSON-friendly string keys and a Map for number or object keys.
Where is Object.fromEntries() documented?
See Object.fromEntries() 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