Record in TypeScript: a practical breakdown
\n\nImagine you are writing an app for learning English words. You need to store translations: cat → кошка, dog → собака. How do you properly type this in TypeScript? You could create an interface, but what if the keys are unknown in advance? Or there are too many of them? That's where Record comes to the rescue — one of the most useful, yet often underestimated, utility types.
In this article, we'll break down what the Record type in TypeScript is, using real-world examples that you can immediately apply in your projects. No boring theory — just practice.
\n\nWhat is Record in TypeScript? In simple terms
\n\nRecord<K, V> is a built-in utility type that creates an object where all keys are of type K and all values are of type V.
Think of it as a typed dictionary or a map with a fixed set of keys.
The basic syntax looks like this:
\n\n// A regular object\nconst userRoles: Record<string, string> = {\n admin: 'Администратор',\n user: 'Пользователь',\n guest: 'Гость'\n};\n\nHere, Record<string, string> means: "An object where keys are strings, and values are also strings." Simple and clear.
Why is Record better than a regular interface?
\n\n- \n
- Key flexibility: You can specify a specific set of keys using a union type. \n
- Conciseness: No need to describe each field manually. \n
- Safety: TypeScript will check that you only use allowed keys. \n
Record vs a plain object: a clear example
\n\nLet's say we have order statuses: pending, shipped, delivered. Without Record, you would have to write an interface with three fields:
interface OrderStatusMap {\n pending: string;\n shipped: string;\n delivered: string;\n}\n\nconst statusDescriptions: OrderStatusMap = {\n pending: 'Ожидает обработки',\n shipped: 'Отправлен',\n delivered: 'Доставлен'\n};\n\nBut with Record, it's more compact and clearer:
\n\ntype OrderStatus = 'pending' | 'shipped' | 'delivered';\n\nconst statusDescriptions: Record<OrderStatus, string> = {\n pending: 'Ожидает обработки',\n shipped: 'Отправлен',\n delivered: 'Доставлен'\n};\n\nPlus: if you try to add cancelled to the object, TypeScript will immediately point out the error — that status doesn't exist in the union type.
Top 5 practical examples of using Record
\n\nLet's look at where Record really simplifies life.
\n\n1. Application configuration
\nStore settings with known keys but different value types:
\n\ntype ConfigKeys = 'apiUrl' | 'timeout' | 'retryCount';\ntype ConfigValue = string | number | boolean;\n\nconst appConfig: Record<ConfigKeys, ConfigValue> = {\n apiUrl: 'https://api.example.com',\n timeout: 5000,\n retryCount: 3\n};\n\n2. Mapping enums or constants
\nOften you need to map a code to its human-readable name:
\n\nenum HttpStatus {\n OK = 200,\n NotFound = 404,\n Error = 500\n}\n\nconst statusMessages: Record<HttpStatus, string> = {\n [HttpStatus.OK]: 'Всё хорошо',\n [HttpStatus.NotFound]: 'Не найдено',\n [HttpStatus.Error]: 'Ошибка сервера'\n};\n\n3. Forms and validation
\nRecord is perfect for storing validation errors by field:
\n\ntype FormFields = 'email' | 'password' | 'name';\ntype ValidationErrors = Record<FormFields, string | null>;\n\nconst errors: ValidationErrors = {\n email: 'Некорректный email',\n password: null,\n name: 'Имя обязательно'\n};\n\n4. Data caching
\nSimulating a cache with identifier keys:
\n\ntype UserI