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