Menu

TypeScript Map: Map<K, V>, get, set, has and Iterate

How to use Map in TypeScript: create a typed Map<K, V>, why get returns V | undefined, set, has and delete, iterating in insertion order, object keys, converting to and from objects and JSON, Map vs object vs Record, and typing array.map().

This page includes runnable editors. Edit, run, and see output instantly.

A TypeScript Map is JavaScript's built-in Map with typed keys and values: Map<string, number> maps strings to numbers. Create one with new Map<K, V>(), then use set, get, has and delete. get returns V | undefined, because the key may be missing.

The type arguments are what make a Map useful in TypeScript: every set is checked and every get returns the value type. The last line is a compile error (TS2345); @ts-expect-error keeps the block running.

This page is about the Map collection. If you searched for array.map(), it is covered in the last section.

Creating a Map

The types come from type arguments, from initial entries, or from an annotation. Initial entries are an array of [key, value] tuples, or anything else that yields them.

Two inference traps:

  • new Map() with no type arguments and no entries is Map<any, any>. Nothing you put in or take out is checked. Always write new Map<K, V>().
  • Entries with different value types do not infer a union. new Map([["a", 1], ["b", "x"]]) is error TS2769 (No overload matches this call). Write the type: new Map<string, number | string>([...]).

get Returns V | undefined

A Map cannot promise that a key exists, so get is typed V | undefined. Under strict, you must deal with the undefined before using the value as a V:

index.ts(4,7): error TS2322: Type 'number | undefined' is not assignable to type 'number'.
  Type 'undefined' is not assignable to type 'number'.

The fixes, from most to least common:

TypeScript does not remember that has returned true when you later call get. Checking the result of get directly is both shorter and safe. A non-null assertion, stock.get("apples")!, silences the error but gives no protection if the key is missing.

Map Methods and Their Types

MemberType on Map<K, V>Notes
new Map<K, V>(entries?)Map<K, V>entries: iterable of [K, V]
set(key, value)thisAdds or replaces; chainable
get(key)V | undefinedundefined when missing
has(key)boolean
delete(key)booleantrue if something was removed
clear()voidRemoves everything
sizenumberA property, not a method
keys(), values()iterators of K, VSpread into an array: [...map.keys()]
entries(), for...ofiterator of [K, V]Insertion order
forEach((value, key) => ...)voidNote: value comes first

Iterating a Map

A Map iterates in insertion order. for...of over the map yields [key, value] tuples, typed [K, V].

Setting an existing key updates the value but keeps its original position in the order.

Object Keys and Counting

Any value can be a key, including objects and arrays. Keys are compared like ===: two objects with the same contents are different keys. A Map is also the standard way to count or group items.

To key by an object's contents, derive a string or number key instead, such as user.id or `${x},${y}`.

Map vs Object vs Record

Map<K, V>Object / Record<string, V>
Key typesanything, compared like ===string (numbers become strings), symbol
Missing key typeget returns V | undefinedobj[key] is V unless noUncheckedIndexedAccess is on
Orderinsertion ordermostly insertion order, but integer-like keys come first in ascending order
Sizemap.sizeObject.keys(obj).length
Frequent add and deleteoptimized for itnot optimized for it
JSONnot directly (JSON.stringify(map) is "{}")direct
Literal syntax, destructuringnoyes
Accidental inherited keysnone"toString" in {} is true

Rule of thumb: a Map for a collection whose keys are data (user ids, words, cache entries) and change at runtime; an object type or Record for a fixed set of known keys and for anything that goes to or from JSON. The dictionary page compares index signatures, Record and Map for string-keyed lookups.

Converting Maps to Objects and JSON

A Map's entries are not properties, so JSON.stringify does not see them. Convert through Object.fromEntries and Object.entries:

JSON.parse returns any, so the as Record<...> states what the data is expected to be. It is not a runtime check; validate untrusted JSON before trusting that type.

Typing array.map()

Many searches for "typescript map" mean the array method, which transforms each element and returns a new array. Its type is inferred from the callback, so annotations are rarely needed:

Annotating the callback's return type ((u): Option => ...) is the clearest way to state the result type: a missing or misspelled property in the returned object is then a compile error at the callback.

Frequently Asked Questions

How do you create a Map in TypeScript?

Pass the key and value types to the constructor: const ages = new Map<string, number>(). With initial entries, the types are inferred: new Map([["ada", 36]]) is a Map<string, number>. A bare new Map() with no types and no entries is Map<any, any>, which turns off checking, so always give it types.

Why does Map.get return undefined in TypeScript?

map.get(key) is typed V | undefined because the key might not be there. TypeScript does not connect an earlier map.has(key) to a later get, so even after has you need to handle undefined: store the result and check it, or use a default with ??.

What is the difference between Map and object in TypeScript?

A Map accepts keys of any type (including objects), keeps insertion order, has a size, and is built for frequent adds and deletes. A plain object or Record<string, V> only has string (and symbol) keys, serializes to JSON directly, and supports literal syntax and destructuring. Use a Map for dynamic keyed collections, an object for fixed shapes and JSON data.

How do I convert a Map to an object or JSON in TypeScript?

Object.fromEntries(map) turns a Map<string, V> into a plain object, which JSON.stringify can then serialize. JSON.stringify(map) on the Map itself gives "{}", because a Map's entries are not properties. The reverse is new Map(Object.entries(obj)).

How do I type the callback of array.map in TypeScript?

Usually you do not need to: items.map((item) => item.name) infers item from the array and the result type from what the callback returns. To force a result type, pass it as a type argument, items.map<string>(...), or annotate the callback's return type.

Coddy programming languages illustration

Learn to code with Coddy

Get started