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 isMap<any, any>. Nothing you put in or take out is checked. Always writenew 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
| Member | Type on Map<K, V> | Notes |
|---|---|---|
new Map<K, V>(entries?) | Map<K, V> | entries: iterable of [K, V] |
set(key, value) | this | Adds or replaces; chainable |
get(key) | V | undefined | undefined when missing |
has(key) | boolean | |
delete(key) | boolean | true if something was removed |
clear() | void | Removes everything |
size | number | A property, not a method |
keys(), values() | iterators of K, V | Spread into an array: [...map.keys()] |
entries(), for...of | iterator of [K, V] | Insertion order |
forEach((value, key) => ...) | void | Note: 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 types | anything, compared like === | string (numbers become strings), symbol |
| Missing key type | get returns V | undefined | obj[key] is V unless noUncheckedIndexedAccess is on |
| Order | insertion order | mostly insertion order, but integer-like keys come first in ascending order |
| Size | map.size | Object.keys(obj).length |
| Frequent add and delete | optimized for it | not optimized for it |
| JSON | not directly (JSON.stringify(map) is "{}") | direct |
| Literal syntax, destructuring | no | yes |
| Accidental inherited keys | none | "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.