Map.getOrInsert(): el método que siempre quisiste en JavaScript
Tiempo estimado de lectura: 4 min
- Menos código, más intención: getOrInsert/getOrInsertComputed expresan “devuelve o crea” en una llamada atómica.
- Mejor tipado en TypeScript 6.0: el retorno es
T, noT | undefined, reduciendo aserciones peligrosas. - Uso práctico: útil en cachés, agrupaciones, contadores y construcción de grafos; evita inicializaciones innecesarias.
Resumen rápido (lectores con prisa)
Map.getOrInsert y Map.getOrInsertComputed permiten obtener un valor existente o insertar uno nuevo en una sola operación. En TypeScript 6.0, con target: "esnext" y lib: ["esnext"], estos métodos están tipados para devolver T en vez de T | undefined. Use getOrInsert cuando quiera expresar “devuelve o crea” de forma atómica; use la variante computada para evitar inicializaciones costosas salvo que sean necesarias.
Map.getOrInsert(): el método que siempre quisiste en JavaScript (qué es y cómo funciona)
Map.getOrInsert(): el método que siempre quisiste en JavaScript aparece en la primera línea porque no es una mejora menor: es la forma de expresar, de manera atómica y tipada, la intención que antes requería tres operaciones verbosas. TypeScript 6.0 ya expone los tipos vía esnext y la propuesta ECMAScript que la introduce ha alcanzado Stage 4. Aquí te explico qué hace, por qué importa y cómo integrarlo con criterio en código de producción.
Qué hace y cómo funciona
El patrón clásico —buscar, insertar si no existe, y luego usar— se repite en cachés, agrupaciones, contadores y grafos. Tradicionalmente escribíamos:
if (!map.has(key)) map.set(key, compute());
const v = map.get(key)!;
Tres operaciones, doble lookup y una aserción no nula que silencia al compilador. getOrInsert y su variante perezosa getOrInsertComputed resuelven eso en una sola llamada:
map.getOrInsert(key, defaultValue): devuelve el valor si existe, si no inserta y devuelve el defaultValue.map.getOrInsertComputed(key, () => value): ejecuta la función sólo si la clave no existía, evitando inicializaciones innecesarias y efectos secundarios.
En TypeScript 6.0, usando target: "esnext" y lib: ["esnext"], el compilador tipa estos métodos de forma que el retorno es T (no T | undefined). El contrato refleja la intención: si llamas, obtienes un valor seguro.
Por qué esto no es solo “azúcar sintáctico”
Tres razones prácticas:
1. Menos errores de tipo
Con el patrón manual el compilador ve get() como T | undefined. Con getOrInsert obtienes T. Menos comprobaciones defensivas, menos ! peligrosos.
2. Menos overhead y operaciones atomizadas
Evitas doble lookup (has + get). Aunque la mejora de rendimiento es pequeña en muchos casos, en bucles grandes o inicializaciones masivas suma.
3. Menos ruido cognitivo
La intención queda expresada: “devuelve o crea”. El lector del código no necesita reconstruir el propósito tras tres líneas.
Casos reales donde cambia la vida
– Cachés con inicialización costosa: evita ejecutar la función de cálculo salvo que sea estrictamente necesario.
– Agrupaciones: Map<string, T[]> ya no requiere crear arrays temporales.
– Contadores y histogramas: inicializar un contador a 0 en una sola línea es más legible.
– Construcción de grafos (listas de adyacencia): evita boilerplate en algoritmos BFS/DFS.
Ejemplo típico
const groups = new Map<string, User[]>();
function addUser(role: string, user: User) {
groups.getOrInsertComputed(role, () => []).push(user);
}
Sencillo, explícito y sin crear arrays inútiles.
Integración práctica en repositorios TypeScript
- Habilita
esnexten tsconfig:{ "compilerOptions": { "target": "esnext", "lib": ["esnext"], "strict": true } } - Revisa compatibilidad runtime:
TypeScript te da la comprobación estática. La API como tal puede requerir polyfill o verificar la versión de Node/Bun/deno si no está implementada nativamente en tu motor.
- Refactor incremental:
Busca utilidades internas con nombres como
getOrCreate,getOrInitocomputeIfAbsent. Reemplaza utilidades por el método nativo en PRs pequeños. Añade pruebas unitarias para casos bordes (excepciones en la función de cálculo, concurrencia en entornos compartidos). - No abuses de
getOrInsertComputedcon side effects:La función de cálculo se invoca solo cuando hace falta, pero debe ser determinista y preferiblemente sin efectos secundarios que el mapa pueda registrar de forma incompleta si ocurre una excepción.
Consideraciones de diseño y criterio Dominicode
Estándar sobre conveniencia: si ya tienes utilidades internas, migrar a getOrInsert reduce mantenimiento y mejora la expresividad.
No sustituyas estructuras lógicas: usar Map con getOrInsert no es excusa para diseños pobres. Sigue definiendo responsabilidades y límites de módulo.
Audita y documenta: añade esta patrón a las guías internas y revisiones de código para homogenizar su uso.
Fuentes y lectura adicional
- Propuesta ECMAScript (upsert/getOrInsert)
- TypeScript releases (para verificar TypeScript 6.0)
- MDN — Map
Adoptar Map.getOrInsert() hoy es reducir ruido, reforzar tipos y expresar intención. No es una moda: es una higiene mínima que facilita mantener bases de código grandes sin perder claridad. Si eres Tech Lead, estandariza su uso; si eres desarrollador, empieza a buscar esos utils/mapGetOrCreate.ts y reemplázalos por la API del lenguaje.
FAQ
- ¿Qué devuelve getOrInsert si la clave existe?
- ¿En qué se diferencia getOrInsert de getOrInsertComputed?
- ¿Necesito un polyfill para usarlo en producción?
- ¿Cómo mejora el tipado en TypeScript 6.0?
- ¿Es seguro usarlo en entornos concurrentes?
- ¿Debo reemplazar todas mis utilidades internas?
- ¿Qué precauciones al usar funciones con efectos secundarios?
Devuelve el valor existente asociado a la clave. El contrato asegura que el retorno es T, no T | undefined.
getOrInsert recibe un valor por defecto ya construido; getOrInsertComputed recibe una función que se ejecuta solo si la clave no existe, evitando costes de inicialización cuando no son necesarios.
Depende del runtime. TypeScript ofrece comprobación estática cuando apuntas a esnext, pero la API puede no estar presente en versiones antiguas de Node/Bun/deno; en esos casos se requiere polyfill o verificación de la versión del motor.
Con target: "esnext" y lib: ["esnext"], los tipos para estos métodos hacen que el retorno sea T, evitando la necesidad de aserciones (!) y reduciendo comprobaciones manuales de undefined.
El método evita doble lookup, pero la seguridad en entornos concurrentes depende del runtime y del modelo de concurrencia. Añade pruebas y, si corresponde, mecanismos de sincronización en entornos compartidos.
No necesariamente. Migra en PRs pequeños y cuando aporte claridad o reduzca mantenimiento. Prioriza casos donde el beneficio sea evidente (cachés, agrupaciones, contadores).
La función de cálculo en getOrInsertComputed debe ser determinista y preferiblemente sin efectos secundarios. Si lanza una excepción, el mapa podría quedar en un estado intermedio; maneja errores y añade pruebas para esos casos.

Leave a Reply