Type expression

Map, Set and WeakMap

  • Collections
  • Tested with TypeScript 5.9

Map<K, V>, Set<T>, WeakMap<K, V> and WeakSet<T> take their key and value types as type arguments. In a .js file you cannot write new Map<string, number>(), so give the variable the type with @type {Map<string, number>} and create it with new Map().

Without a type and without initial entries, new Map() is Map<any, any> and checks nothing, as the last example shows. With initial entries, new Map([["TEE-01", 12]]) infers Map<string, number> from them.

At a glance

Use JSDoc in a .js file TypeScript in a .ts file
A Map /** @type {Map<string, number>} */ const stock = new Map(); const stock = new Map<string, number>();
A Set /** @type {Set<string>} */ const tags = new Set(); const tags = new Set<string>();
A WeakMap /** @type {WeakMap<HTMLElement, number>} */ : WeakMap<HTMLElement, number>

Examples

cache.js cache.ts TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
/** @type {Map<string, number>} */
const stock = new Map([["TEE-01", 12]]);

/** @type {Set<string>} */
const tags = new Set(["cotton", "summer"]);

/** @type {WeakMap<HTMLElement, { clicks: number }>} */
const stats = new WeakMap();

stock.set("MUG-07", 3);
tags.add("sale");

/** @param {HTMLElement} el */
export function track(el) {
  const entry = stats.get(el) ?? { clicks: 0 };
  entry.clicks += 1;
  stats.set(el, entry);
}

export { stock, tags };
const stock: Map<string, number> = new Map([["TEE-01", 12]]);

const tags: Set<string> = new Set(["cotton", "summer"]);

const stats: WeakMap<HTMLElement, { clicks: number }> = new WeakMap();

stock.set("MUG-07", 3);
tags.add("sale");

export function track(el: HTMLElement) {
  const entry = stats.get(el) ?? { clicks: 0 };
  entry.clicks += 1;
  stats.set(el, entry);
}

export { stock, tags };
Map, Set and WeakMap with their type arguments.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
stock.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: stock.js(6,21): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. stock.js(6,21): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. stock.js(6,21): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. checked 21 October 2026
/** @type {Map<string, number>} */
const stock = new Map();

stock.set("TEE-01", 12);
stock.set("MUG-07", "3");error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

export { stock };
The value type of a Map is checked on set().
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
stock.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: stock.js(5,14): error TS2532: Object is possibly 'undefined'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. stock.js(5,14): error TS2532: Object is possibly 'undefined'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. stock.js(5,14): error TS2532: Object is possibly 'undefined'. checked 21 October 2026
/** @type {Map<string, number>} */
const stock = new Map([["TEE-01", 12]]);

const left = stock.get("TEE-01") - 1;error TS2532: Object is possibly 'undefined'.

const safe = (stock.get("TEE-01") ?? 0) - 1;

export { left, safe };
Map.get() can return undefined.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
stock.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
// No type: both lines pass, because the Map is Map<any, any>.
const stock = new Map();
stock.set(1, "twelve");

export { stock };
new Map() without a type is Map<any, any>.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

get() may miss

Map.prototype.get returns V | undefined, so using the result directly is an error (TS2532 in the third example). Handle the miss with ??, an if, or a has() check followed by a get() (TypeScript does not connect the two calls, so you may still need the ??).

WeakMap and WeakSet

Weak collections need object keys, so their key type must be an object type such as HTMLElement or object; a string key is an error. They suit data attached to DOM nodes or other objects you do not own.

Gotchas

  • Class fields work the same way: /** @type {Map<string, Product>} */ #byId = new Map();.
  • Iterating a Map gives [K, V] tuples, so for (const [sku, count] of stock) types both.

Questions

How do I type a Map in JSDoc?

Put the type on the variable: /** @type {Map<string, number>} */ const stock = new Map();. The type arguments then apply to every get, set and loop.

Sources

Last reviewed 20 October 2026.