Un Map de TypeScript es el Map integrado de JavaScript con claves y valores tipados: Map<string, number> asocia strings a números. Crea uno con new Map<K, V>() y luego usa set, get, has y delete. get devuelve V | undefined, porque puede que la clave no exista.
Los argumentos de tipo son lo que hace útil un Map en TypeScript: cada set se comprueba y cada get devuelve el tipo del valor. La última línea es un error de compilación (TS2345); @ts-expect-error mantiene el bloque en marcha.
Esta página trata de la colección Map. Si buscabas array.map(), está en la última sección.
Crear un Map
Los tipos vienen de los argumentos de tipo, de las entradas iniciales o de una anotación. Las entradas iniciales son un array de tuplas [key, value], o cualquier otra cosa que las produzca.
Dos trampas de inferencia:
new Map()sin argumentos de tipo ni entradas esMap<any, any>. No se comprueba nada de lo que metes ni de lo que sacas. Escribe siemprenew Map<K, V>().- Las entradas con valores de distinto tipo no infieren una unión.
new Map([["a", 1], ["b", "x"]])es el error TS2769 (No overload matches this call). Escribe el tipo:new Map<string, number | string>([...]).
get devuelve V | undefined
Un Map no puede garantizar que una clave exista, así que get tiene tipo V | undefined. Con strict, debes ocuparte del undefined antes de usar el valor como V:
index.ts(4,7): error TS2322: Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.
Las soluciones, de más a menos habitual:
TypeScript no recuerda que has devolvió true cuando después llamas a get. Comprobar directamente el resultado de get es más corto y seguro. Una aserción non-null, stock.get("apples")!, silencia el error pero no protege de nada si la clave falta.
Métodos de Map y sus tipos
| Miembro | Tipo en Map<K, V> | Notas |
|---|---|---|
new Map<K, V>(entries?) | Map<K, V> | entries: iterable de [K, V] |
set(key, value) | this | Añade o sustituye; encadenable |
get(key) | V | undefined | undefined si falta |
has(key) | boolean | |
delete(key) | boolean | true si se eliminó algo |
clear() | void | Lo elimina todo |
size | number | Una propiedad, no un método |
keys(), values() | iteradores de K, V | Expándelos en un array: [...map.keys()] |
entries(), for...of | iterador de [K, V] | Orden de inserción |
forEach((value, key) => ...) | void | Ojo: el valor va primero |
Recorrer un Map
Un Map se recorre en orden de inserción. for...of sobre el map produce tuplas [key, value], de tipo [K, V].
Asignar una clave que ya existe actualiza el valor pero mantiene su posición original en el orden.
Claves que son objetos y contar elementos
Cualquier valor puede ser una clave, incluidos objetos y arrays. Las claves se comparan como con ===: dos objetos con el mismo contenido son claves distintas. Un Map también es la forma estándar de contar o agrupar elementos.
Para usar el contenido de un objeto como clave, deriva una clave string o numérica, como user.id o `${x},${y}`.
Map frente a objeto y Record
Map<K, V> | Objeto / Record<string, V> | |
|---|---|---|
| Tipos de clave | cualquiera, comparada como con === | string (los números pasan a strings), symbol |
| Tipo si falta la clave | get devuelve V | undefined | obj[key] es V salvo que noUncheckedIndexedAccess esté activado |
| Orden | orden de inserción | casi siempre de inserción, pero las claves que parecen enteros van primero en orden ascendente |
| Tamaño | map.size | Object.keys(obj).length |
| Añadir y borrar con frecuencia | optimizado para ello | no optimizado para ello |
| JSON | no directamente (JSON.stringify(map) es "{}") | directo |
| Sintaxis literal, desestructuración | no | sí |
| Claves heredadas por accidente | ninguna | "toString" in {} es true |
Regla práctica: un Map para una colección cuyas claves son datos (ids de usuario, palabras, entradas de caché) y cambian en tiempo de ejecución; un tipo objeto o Record para un conjunto fijo de claves conocidas y para todo lo que va a o viene de JSON. La página de diccionarios compara las firmas de índice, Record y Map para búsquedas con claves string.
Convertir Maps en objetos y JSON
Las entradas de un Map no son propiedades, así que JSON.stringify no las ve. Convierte con Object.fromEntries y Object.entries:
JSON.parse devuelve any, así que el as Record<...> indica qué se espera que sean los datos. No es una comprobación en tiempo de ejecución; valida el JSON no fiable antes de confiar en ese tipo.
Tipar array.map()
Muchas búsquedas de «typescript map» se refieren al método de array, que transforma cada elemento y devuelve un array nuevo. Su tipo se infiere a partir del callback, así que rara vez hacen falta anotaciones:
Anotar el tipo de retorno del callback ((u): Option => ...) es la forma más clara de indicar el tipo del resultado: una propiedad que falta o está mal escrita en el objeto devuelto pasa a ser un error de compilación en el callback.
Preguntas frecuentes
¿Cómo se crea un Map en TypeScript?
Pasa los tipos de clave y valor al constructor: const ages = new Map<string, number>(). Con entradas iniciales, los tipos se infieren: new Map([["ada", 36]]) es un Map<string, number>. Un new Map() sin tipos ni entradas es Map<any, any>, que desactiva la comprobación, así que dale siempre tipos.
¿Por qué Map.get devuelve undefined en TypeScript?
map.get(key) tiene tipo V | undefined porque puede que la clave no esté. TypeScript no relaciona un map.has(key) anterior con un get posterior, así que incluso después de has tienes que manejar undefined: guarda el resultado y compruébalo, o usa un valor por defecto con ??.
¿Qué diferencia hay entre Map y un objeto en TypeScript?
Un Map acepta claves de cualquier tipo (objetos incluidos), mantiene el orden de inserción, tiene size y está pensado para añadir y borrar con frecuencia. Un objeto normal o Record<string, V> solo tiene claves string (y symbol), se serializa a JSON directamente y admite sintaxis literal y desestructuración. Usa un Map para colecciones dinámicas con clave y un objeto para formas fijas y datos JSON.
¿Cómo convierto un Map en un objeto o en JSON en TypeScript?
Object.fromEntries(map) convierte un Map<string, V> en un objeto normal, que luego JSON.stringify puede serializar. JSON.stringify(map) sobre el propio Map da "{}", porque las entradas de un Map no son propiedades. La operación inversa es new Map(Object.entries(obj)).
¿Cómo tipo el callback de array.map en TypeScript?
Normalmente no hace falta: items.map((item) => item.name) infiere item a partir del array y el tipo del resultado a partir de lo que devuelve el callback. Para forzar un tipo de resultado, pásalo como argumento de tipo, items.map<string>(...), o anota el tipo de retorno del callback.