Map and Set Types in TypeScript

Map and Set are typed collections that track keys and unique values. Learn how to type Map<K, V> and Set<T>, how they differ from objects and arrays, and common mistakes.

5 min read

TypeScript Map and Set types are typed collection classes that TypeScript enhances with generic type parameters. A Map is a key-value store where both the key and value have types. A Set is a collection of unique values where every element has a single type.

Unlike plain objects, Map keys can be any type -- strings, numbers, objects, or even other Maps. Unlike arrays, a Set automatically prevents duplicate values and provides fast membership checking. Both classes exist at runtime and are not erased by the compiler.

Typing a Set

A Set is typed with one generic parameter -- the type of values it stores. Use the Set generic syntax, which is similar to how you type an Array with a single type parameter.

typescripttypescript
let names: Set<string> = new Set(["Ada", "Linus", "Grace"]);

TypeScript infers the element type from the initial values, so the explicit annotation is often optional. This declaration produces the same type as the one above:

typescripttypescript
let names = new Set(["Ada", "Linus", "Grace"]);

TypeScript infers that names is a Set<string>. Every method that adds or returns elements respects this type.

The add method enforces the element type. You can only add values that match the declared type:

typescripttypescript
let scores = new Set([95, 87, 91]);
 
scores.add(100);
scores.add("ninety");

The first call succeeds because 100 is a number. The second call is a compiler error because a string does not match the number element type.

texttext
Argument of type 'string' is not assignable to parameter of type 'number'.

The has method checks membership and returns a boolean. The compiler does not care what type you check -- has always returns true or false -- but the check is only meaningful for the declared type. Iterating a Set with a for-of loop gives you each element with its correct type:

typescripttypescript
let colors = new Set(["red", "green", "blue"]);
 
for (let color of colors) {
  console.log(color.toUpperCase());
}

Color is typed as string inside the loop because the Set element type is string. You can use string methods directly without any type guard.

Typing a Map

A Map is typed with two generic parameters: the key type and the value type. The syntax is Map with the key type first, then the value type in angle brackets.

typescripttypescript
let users: Map<number, string> = new Map([
  [1, "Ada"],
  [2, "Linus"],
]);

TypeScript infers both type parameters from the initial entries, so the annotation is optional if you initialise the Map with values. Without initial values, annotate both type parameters so the compiler knows what to expect.

The set method enforces both the key and value types. Every call must match the declared types for both arguments:

typescripttypescript
let ages = new Map<string, number>();
 
ages.set("Ada", 30);
ages.set("Linus", 55);
ages.set(1, 25);

The first two calls succeed. The third call is a compiler error because the key must be a string.

The get method returns the value type or undefined, because the key might not exist in the Map. This is the same pattern as array index access:

typescripttypescript
let ages = new Map<string, number>();
ages.set("Ada", 30);
 
let age = ages.get("Ada");
let missing = ages.get("Unknown");

TypeScript types age as number | undefined and missing as number | undefined. You must check for undefined before using the value as a number. For more on narrowing pattern, see type narrowing basics in TypeScript.

Iterating a Map gives you key-value pairs as tuples. Each entry is typed as [KeyType, ValueType]:

typescripttypescript
let config = new Map<string, boolean>();
config.set("darkMode", true);
config.set("notifications", false);
 
for (let [key, value] of config) {
  console.log(`${key}: ${value}`);
}

The destructured key is typed as string and value is typed as boolean. You get full type safety through every iteration method -- forEach, keys, values, and entries all respect the Map's type parameters.

When to use Map and Set vs alternatives

Map and Set solve specific problems that objects and arrays handle poorly. The decision tree helps you pick the right structure:

Use caseBest choice
Unique values, fast existence checkSet
Key-value storage, non-string keysMap
Key-value storage, string keys only, small datasetsObject
Ordered list with duplicates allowedArray
Fixed-length, per-position typesTuple

Maps shine when you need non-string keys or want to track insertion order explicitly. Sets shine when duplicate removal or fast membership testing is the primary operation. For a detailed comparison of all collection types, see choose between array tuple record map and set.

Common mistakes

The most common mistake is forgetting that Map's get method returns undefined when a key is not found. Always check the result before using it, or provide a fallback value with the nullish coalescing operator.

Another common mistake is creating an empty Map or Set without type parameters. Without initial values to infer from, the compiler uses loose defaults that disable checking. Always annotate empty collections:

typescripttypescript
let untyped = new Map();
let typed: Map<string, number> = new Map();

The first line produces Map<any, any>, which accepts any key or value with no checking. The second line locks the key and value types before anything is added.

The same rule applies to an empty Set. Annotate it with the element type you intend, instead of leaving TypeScript to infer an any-typed set from nothing.

Rune AI

Rune AI

Key Insights

  • Type a Map with Map<K, V> where K is the key type and V is the value type.
  • Type a Set with Set<T> where T is the element type.
  • TypeScript infers Map and Set types from the initial values, same as arrays.
  • Map and Set exist at runtime and are not erased unlike many TypeScript types.
  • Use Map when you need non-string keys; use Set when you need automatic duplicate removal.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between Set and Array in TypeScript?

A Set stores unique values with no duplicates automatically and has constant-time has/delete operations. An Array allows duplicates and has linear-time includes/indexOf. Use Set when uniqueness matters; use Array when ordering or index access matters.

Can I use objects as Map keys in TypeScript?

Yes. Map keys can be any type including objects, while plain object keys are always strings or symbols. Use Map when you need non-string keys or want to track insertion order explicitly.

Are Map and Set types erased at runtime?

Map and Set are runtime JavaScript classes, unlike tuple or readonly types. The generic type parameters Map&lt;K,V&gt; and Set&lt;T&gt; are erased at compile time, but the Map and Set objects exist at runtime.

Conclusion

Map and Set are typed collection classes that exist at runtime with full type safety from TypeScript's generic parameters. Use Set for unique-value collections and Map for key-value stores with any key type. The compiler tracks element types through every add, get, has, and iteration operation.