When you need to store collections of key–value pairs in TypeScript, two popular options are Record and Map. They serve similar purposes—both map keys to values—but differ in syntax, capabilities, and use-cases. Understanding these differences helps you pick the right tool for the job.
1. Using Plain Objects with Index Signatures
Traditionally, JavaScript objects are used as dictionaries:
interface User {
id: string;
name: string;
}
type Users = { [key: string]: User };
const users: Users = {
abc123: { id: 'abc123', name: 'John Doe' },
xyz789: { id: 'xyz789', name: 'Jane Doe' },
};
console.log(users['abc123']); // { id: 'abc123', name: 'John Doe' }
The { [key: string]: User } syntax is called an index signature. It tells TypeScript that users can have any number of string keys, each mapping to a User.
2. Record<K, T> – A Cleaner Type Alias
Record is a built-in TypeScript utility type that makes the above pattern more concise:
interface User {
id: string;
name: string;
}
type Users = Record<string, User>;
const users: Users = {
abc123: { id: 'abc123', name: 'John Doe' },
xyz789: { id: 'xyz789', name: 'Jane Doe' },
};
Benefits of Record:
- Shorter, easier to read than an index signature.
- Lets you constrain keys if you want a fixed set:
type Roles = Record<'admin' | 'editor', User>; - Perfect for plain JSON-like objects where keys are strings (or numbers/symbols) known at compile time.
Limitations:
- Keys are always strings (or a union of string literals).
- Methods such as
.sizeor ordered iteration aren’t built in—you treat it like a normal object.
3. Map<K, V> – A Full-Featured Key–Value Store
ECMAScript Map is a class that offers more flexibility than a plain object:
interface User {
id: string;
name: string;
}
const usersMap = new Map<string, User>();
usersMap.set('abc123', { id: 'abc123', name: 'John Doe' });
usersMap.set('xyz789', { id: 'xyz789', name: 'Jane Doe' });
console.log(usersMap.get('abc123')); // { id: 'abc123', name: 'John Doe' }
Advantages of Map:
- Keys can be any value, not just strings (objects, numbers, functions, etc.).
- Maintains insertion order, which can be important for iteration.
- Provides convenient methods:
.get,.set,.has,.delete,.size, and built-in iterators.
Trade-offs:
- Slightly more overhead than a plain object.
- Not as easy to serialize to JSON directly (
JSON.stringifywon’t automatically include map entries).
4. When to Use Which
| Scenario | Prefer |
|---|---|
| Static or JSON-like data, simple string keys | Record |
| Need arbitrary key types (objects, numbers, etc.) | Map |
| Require ordered iteration or frequent insert/delete | Map |
| Want lightweight, serializable structure | Record |
Quick Recap
-
Record<K, V>: TypeScript utility for object literals where keys are strings or a union of string literals. Clean and lightweight. -
Map<K, V>: ECMAScript class with rich methods, supports any key type and preserves insertion order.
Choosing between them comes down to your requirements:
- For configuration data, caching simple IDs, or working with JSON:
Record(or an index signature) is perfect. - For dynamic, runtime-driven collections with complex keys or heavy mutation:
Mapis the better fit.
By understanding these tools, you can model key–value data in TypeScript with the right balance of simplicity and power.
Top comments (0)