Map en TypeScript: una colección clave-valor tipada
Map en TypeScript
Map en TypeScript: tipar, recorrer y guardar un Map | We.Inc
Tipa un Map<K, V> en TypeScript, recórrelo, conviértelo a JSON y de vuelta, y decide entre Map, Record y un objeto simple. Prueba un Map en vivo en el navegador.
En TypeScript, un Map es el Map normal de JavaScript con tipos para sus claves y valores: new Map<string, number>(). Las claves pueden ser cualquier valor, las entradas vuelven en el orden en que se añadieron por primera vez, size las cuenta y get devuelve V | undefined. JSON.stringify no serializa un Map, así que conviértelo antes.
Métodos de Map y lo que devuelve TypeScript
Llamada
Qué hace
Tipo que obtienes (Map<string, number>)
map.set(k, v)
Añade o actualiza una entrada
Map<string, number> (encadenable)
map.get(k)
Lee un valor
number | undefined
map.has(k)
Comprueba una clave
map.delete(k)
Elimina una entrada
boolean (true si existía)
Cuenta las entradas
for (const [k, v] of map)
Recorre en orden de inserción
k: string, v: number
El playground de abajo es un Map real que se ejecuta en tu navegador. Añade, actualiza y borra entradas y fíjate en tres cosas: el orden que mantienen las entradas, lo que indica size y la diferencia entre aplicar stringify al propio Map y a una copia convertida.
Escribe una clave y un número y pulsa Establecer para añadir o actualizar esa entrada.
Pulsa Borrar para eliminar la clave del campo de clave, o Vaciar para dejar el Map sin entradas.
Lee la lista de entradas, que sigue el orden de iteración del propio Map.
Compara las dos líneas de JSON para ver por qué un Map necesita convertirse antes de guardarlo o enviarlo.
Indica los argumentos genéricos al crear un Map vacío. new Map() por sí solo es Map<any, any>, y cada lectura posterior pierde su tipo.
Comprueba has() o compara get() con undefined antes de usar un valor. Una aserción non-null oculta el caso en que la clave realmente falta.
Las claves que son objetos se comparan por referencia. Dos objetos con los mismos campos son dos claves distintas, así que usa un id en forma de cadena cuando te refieras al mismo registro.
Para guardar un Map en localStorage, almacena JSON.stringify([...map]) y reconstrúyelo con new Map(JSON.parse(text)). El array de pares conserva las claves que no son cadenas y el orden.
Preguntas frecuentes
¿Cómo declaro el tipo de un Map en TypeScript?
Pasa los tipos de clave y valor como argumentos genéricos: const prices = new Map<string, number>(). Para un campo o un parámetro, escribe el tipo como Map<string, number>, o ReadonlyMap<string, number> cuando la función solo deba leerlo.
¿Por qué map.get() incluye undefined en su tipo?
Porque el compilador no puede saber qué claves hay en el Map en un momento dado. get está tipado como V | undefined, así que acotas con un if, un valor alternativo con ?? o una comprobación has() antes de tratar el valor como V.
¿Por qué JSON.stringify(map) da {}?
Un Map guarda sus entradas en un espacio interno, no como propiedades de objeto, así que JSON ve un objeto vacío. Conviértelo antes: Object.fromEntries(map) para claves de tipo cadena, o [...map] para un array de pares que funciona con cualquier tipo de clave.
¿Uso un Map o un Record?
Usa un Record para un conjunto fijo de claves con nombre y para datos que van y vienen de JSON. Usa un Map cuando las entradas aparecen y desaparecen mientras se ejecuta la aplicación, cuando las claves son números u objetos, o cuando importa el orden de inserción.
¿Actualizar una clave la mueve al final?
No. Establecer una clave existente cambia su valor pero mantiene su posición original. Para moverla al final, borra la clave y vuelve a establecerla.
Map, Record y un objeto simple
Un objeto simple hereda de Object.prototype, así que nombres como constructor ya están ahí, y sus claves siempre son cadenas o símbolos. Un Map empieza vacío, acepta cualquier valor como clave e indica su número de entradas mediante size.
Record es un tipo, no un contenedor. Describe un objeto simple, así que todo lo anterior sobre los objetos se le aplica en tiempo de ejecución, incluido el soporte nativo de JSON que le falta a un Map.
Cómo se comparan las claves
Map compara las claves con la regla SameValueZero. El número 1 y la cadena "1" son claves distintas, NaN coincide con NaN y un objeto solo coincide consigo mismo. Un objeto simple convertiría 1 en "1" y fusionaría ambas.
Eso convierte a Map en la caché natural para valores asociados a un elemento del DOM, una función o un objeto de petición, casos en los que un objeto necesitaría un id de cadena inventado.
Tipar Maps en las firmas de funciones
Acepta ReadonlyMap<K, V> en las funciones que solo leen. Elimina set, delete y clear del tipo, así que la propia firma dice a quien la invoca que su Map no se modificará.
Cuando construyes un Map a partir de un array de pares, TypeScript deduce los tipos de los pares. Añade un genérico explícito cuando el array pueda estar vacío o cuando los pares mezclen tipos que quieres mantener separados.
Recetas con Map tipado
Contar palabras con un Map
El valor alternativo con ?? cubre la primera aparición de una palabra, por eso que get devuelva number | undefined resulta útil en lugar de molesto.
Guardar un Map como JSON y volver a cargarlo
Un array de pares conserva cada entrada y su orden. Object.fromEntries es la opción más legible cuando todas las claves son cadenas.
Aceptar un Map de solo lectura
Dentro de total, invocar prices.set sería un error de compilación, así que quien la usa puede pasar su Map sin hacer una copia defensiva.
Playground de Map en vivo
Clave
Valor (número)
Establecer
Borrar
Vaciar
Restablecer
Entradas en orden de iteración
El Map está vacío.
size: {n}
Se añadió "{v}".
Se borró "{v}".
"{v}" no está en el Map, así que delete devolvió false.