TypeScript Maps
last modified October 15, 2026
A Map is a collection of key-value pairs. Unlike a plain
object, a map accepts keys of any type, including numbers and objects, keeps
the insertion order of its entries, and has a size property and
methods for reading and changing entries. In TypeScript, the type
Map<K, V> states the type of the keys K and of
the values V.
Creating Maps
A map is created with the Map constructor, either
empty or from an array of key-value pairs.
const userRoles = new Map<number, string>();
userRoles.set(1, "Admin");
userRoles.set(2, "Editor");
const productPrices = new Map([
["Laptop", 999],
["Mouse", 29],
]);
console.log(userRoles);
console.log(productPrices);
console.log(userRoles.size, productPrices.size);
The empty map needs explicit type arguments: without
<number, string>, TypeScript would not know what it will
contain. For productPrices, the types are inferred from the
initial pairs, so its type is Map<string, number>. The
size property returns the number of entries.
$ npx tsx creating_maps.ts
Map(2) { 1 => 'Admin', 2 => 'Editor' }
Map(2) { 'Laptop' => 999, 'Mouse' => 29 }
2 2
Adding and Updating Entries
The set method adds a new entry or replaces the value
of an existing key.
const inventory = new Map<string, number>();
inventory.set("Apples", 50).set("Oranges", 30);
inventory.set("Apples", 45);
console.log(inventory);
// inventory.set("Pears", "many"); // Error: 'string' is not assignable to 'number'
The set method returns the map itself, so calls can be
chained. Setting "Apples" again replaces its value; a key occurs
in a map at most once. The commented line would not compile, because the
values must be numbers.
$ npx tsx adding_entries.ts
Map(2) { 'Apples' => 45, 'Oranges' => 30 }
Accessing Values
The get method returns the value for a key. If the key
is missing, it returns undefined.
const capitals = new Map<string, string>([
["France", "Paris"],
["Japan", "Tokyo"],
]);
console.log(capitals.get("Japan"));
console.log(capitals.get("Germany"));
const capital = capitals.get("France");
console.log(capital?.toUpperCase());
console.log(capitals.get("Germany") ?? "unknown");
The return type of get is
string | undefined. Because of this, TypeScript does not allow
capital.toUpperCase() without a check; optional chaining or the
?? operator handle the missing case.
$ npx tsx accessing_values.ts Tokyo undefined PARIS unknown
Checking Key Existence
The has method tells whether a key is present.
const colors = new Map<string, string>([
["red", "#ff0000"],
["empty", ""],
]);
console.log(colors.has("red"), colors.has("blue"));
for (const name of ["red", "empty", "blue"]) {
const hex = colors.get(name);
console.log(name, hex !== undefined ? `found '${hex}'` : "missing");
}
When reading a value, it is often simpler to call get
and compare the result with undefined than to call
has first. Note that the test must not be a plain truthiness
check: the empty string stored under "empty" is a real value,
but it is falsy.
A has check does not narrow the result of a later
get call; the compiler still types it as
string | undefined.
$ npx tsx checking_keys.ts true false red found '#ff0000' empty found '' blue missing
Removing Entries
The delete method removes one entry and
clear removes all of them.
const settings = new Map<string, boolean>([
["darkMode", true],
["notifications", false],
["autosave", true],
]);
console.log(settings.delete("notifications"));
console.log(settings.delete("missing"));
console.log(settings);
settings.clear();
console.log(settings.size);
The delete method returns true when the
key existed and was removed, and false otherwise. Both methods
change the map in place.
$ npx tsx removing_entries.ts
true
false
Map(2) { 'darkMode' => true, 'autosave' => true }
0
Iterating Maps
Maps are iterable. They yield their entries in insertion order.
const users = new Map<number, string>([
[101, "Alice"],
[102, "Bob"],
[103, "Carol"],
]);
for (const [id, name] of users) {
console.log(`${id}: ${name}`);
}
users.forEach((name, id) => {
console.log(`${name} has id ${id}`);
});
console.log([...users.keys()], [...users.values()]);
A for...of loop over a map yields
[key, value] pairs, which are destructured into id
and name. The forEach callback receives the value
first and the key second. The keys and values
methods return iterators, which the spread operator turns into arrays.
$ npx tsx iterating_maps.ts 101: Alice 102: Bob 103: Carol Alice has id 101 Bob has id 102 Carol has id 103 [ 101, 102, 103 ] [ 'Alice', 'Bob', 'Carol' ]
Converting Maps
Maps convert easily to arrays and to plain objects, and back.
const scoreboard = new Map<string, number>([
["Team A", 3],
["Team B", 5],
["Team C", 1],
]);
const entries = [...scoreboard];
console.log(entries);
const ranking = [...scoreboard].sort((a, b) => b[1] - a[1]).map(([team]) => team);
console.log(ranking);
const asObject = Object.fromEntries(scoreboard);
console.log(asObject, JSON.stringify(asObject));
const backToMap = new Map(Object.entries(asObject));
console.log(backToMap.get("Team B"));
Spreading a map produces an array of pairs of type
[string, number][], which can be sorted and transformed with
array methods. Object.fromEntries builds an object from a map,
which is needed for JSON.stringify: a map itself is serialized
as an empty object. Object.entries and the Map
constructor do the reverse.
$ npx tsx map_to_array.ts
[ [ 'Team A', 3 ], [ 'Team B', 5 ], [ 'Team C', 1 ] ]
[ 'Team B', 'Team A', 'Team C' ]
{ 'Team A': 3, 'Team B': 5, 'Team C': 1 } {"Team A":3,"Team B":5,"Team C":1}
5
Grouping and Counting
Maps are well suited for counting and grouping values.
const words = ["apple", "avocado", "banana", "blueberry", "cherry", "apricot"];
const counts = new Map<string, number>();
for (const word of words) {
const letter = word[0];
counts.set(letter, (counts.get(letter) ?? 0) + 1);
}
console.log(counts);
const groups = new Map<number, string[]>();
for (const word of words) {
const list = groups.get(word.length) ?? [];
list.push(word);
groups.set(word.length, list);
}
console.log(groups);
For counting, the current count is read with get, a
missing key defaults to 0 with ??, and the incremented value is
stored back. Grouping works the same way with arrays as values; here the
words are grouped by their length, a number key. Newer runtimes also offer
Map.groupBy for the second task.
$ npx tsx grouping.ts
Map(3) { 'a' => 3, 'b' => 2, 'c' => 1 }
Map(4) {
5 => [ 'apple' ],
7 => [ 'avocado', 'apricot' ],
6 => [ 'banana', 'cherry' ],
9 => [ 'blueberry' ]
}
Objects as Keys
Map keys can be objects. They are compared by reference, not by content.
type User = { name: string };
const alice: User = { name: "Alice" };
const bob: User = { name: "Bob" };
const lastLogin = new Map<User, string>();
lastLogin.set(alice, "2026-10-14");
lastLogin.set(bob, "2026-10-15");
console.log(lastLogin.get(alice));
console.log(lastLogin.get({ name: "Alice" }));
The map associates data with objects without adding properties to
them. The second lookup returns undefined: the object literal
has the same content as alice, but it is a different object. A
plain object cannot do this at all, because its keys are always strings or
symbols. For caches tied to objects, a WeakMap lets the keys be
garbage-collected.
$ npx tsx object_keys.ts 2026-10-14 undefined
Map versus Record
For a fixed or string-keyed set of entries, a plain object typed
with Record is often enough.
type Currency = "EUR" | "USD" | "GBP";
const symbols: Record<Currency, string> = {
EUR: "€",
USD: "$",
GBP: "£",
};
const rates = new Map<Currency, number>();
rates.set("USD", 1.08);
console.log(symbols.EUR, Object.keys(symbols).length);
console.log(rates.get("USD"), rates.get("GBP"), rates.size);
The Record<Currency, string> type requires a
value for every currency, and symbols.EUR has the type
string. A map can hold any subset of keys, so
rates.get always returns number | undefined. Use
an object when the keys are known in advance and the data is mostly read;
use a map when entries are added and removed at runtime, when keys are not
strings, or when the order and size matter.
$ npx tsx map_vs_record.ts € 3 1.08 undefined 1
Best Practices
- Give Empty Maps Type Arguments: Write
new Map<K, V>(); let the compiler infer the types of maps created with initial entries. - Handle Missing Keys:
getreturnsV | undefined; use??for defaults or compare withundefined. - Avoid Truthiness Checks: Values such as
0and""are valid; check forundefinedinstead. - Convert for JSON: Use
Object.fromEntriesbefore serializing a map. - Choose Map or Record Deliberately: Use maps for dynamic collections and non-string keys, objects for fixed structures.
Source
This tutorial covered maps in TypeScript. With type parameters, the compiler checks every key and value that goes into a map and knows the type of everything that comes out of it.
Author
List all TypeScript tutorials.