TYPESCRIPT

TypeScript Record Type: Syntax, Use Cases, and Examples

The TypeScript Record type provides a convenient way to define objects with consistent key and value types. It's especially useful when you want to map a fixed set of keys to specific types, enabling clear, predictable object structures in your code. Instead of manually typing each key in an object, you can use Record TypeScript to generate structured data mappings with type safety and flexibility.

In this article, you'll learn how the TypeScript Record type works, how to use it effectively, and when to consider alternatives like Map or custom interfaces. This guide includes syntax, examples, and comparisons to help you write more maintainable and type-safe code using this versatile utility type.


What Is the TypeScript Record Type?

The Record utility type in TypeScript is a built-in generic type that creates an object type with a fixed set of keys and a specified value type. Instead of manually defining an interface like:

interface FruitColors {
  apple: string;
  banana: string;
  cherry: string;
}

You can use Record like this:

type FruitColors = Record<'apple' | 'banana' | 'cherry', string>;

This structure ensures all listed keys exist and are assigned a value of the specified type. It is particularly helpful for transforming unions into concrete object types or when dynamically defining structures in more concise syntax.


Syntax of TypeScript Record

The basic syntax of a TypeScript Record is:

Record<Keys, Type>

Here, Keys is a union of property names (often strings or numbers), and Type is the type of values associated with those keys.

Example:

type RolePermissions = Record<'admin' | 'editor' | 'viewer', boolean>;

This creates an object type with the exact keys 'admin', 'editor', and 'viewer', where each value is a boolean.


Why Use the Record Type in TypeScript?

The record TypeScript structure makes code easier to reason about when working with fixed key-value maps. It's often used in situations like:

  • Assigning roles to access rights.
  • Mapping keys to component configurations.
  • Creating typed dictionaries where all keys are known up front.
  • Defining enums with associated data types.

Compared to using an index signature like { [key: string]: string }, Record is more explicit and avoids allowing unexpected keys.

It also improves editor support by enabling IntelliSense for all keys in the union. When you use Record, you get warnings if you forget a key or assign a value of the wrong type.


Record TypeScript Example: String Mapping

Let’s consider a case where you’re mapping product SKUs to product names:

type ProductNames = Record<'sku123' | 'sku456' | 'sku789', string>;

const products: ProductNames = {
  sku123: 'Laptop',
  sku456: 'Tablet',
  sku789: 'Smartphone'
};

This ensures that the object always includes all three keys and each is assigned a string value.


Using Enums with Record

You can also use enums as the keys for a TypeScript Record type. This is common in scenarios involving status codes or event types.

enum Status {
  Success = 'SUCCESS',
  Error = 'ERROR',
  Loading = 'LOADING'
}

type StatusMessages = Record<Status, string>;

const messages: StatusMessages = {
  [Status.Success]: 'Operation completed',
  [Status.Error]: 'Something went wrong',
  [Status.Loading]: 'Please wait...'
};

This use of record TypeScript with enums ensures that every enum value is handled, helping prevent missing cases in business logic.


Nesting Records

You can nest records to create more complex structures. For example, if you want to associate multiple permissions with each user role:

type Permissions = 'read' | 'write' | 'delete';
type Roles = 'admin' | 'user' | 'guest';

type RoleAccessMatrix = Record<Roles, Record<Permissions, boolean>>;

const accessMatrix: RoleAccessMatrix = {
  admin: { read: true, write: true, delete: true },
  user: { read: true, write: true, delete: false },
  guest: { read: true, write: false, delete: false }
};

This structure clearly defines what actions each role can perform and uses nested Record types to preserve type safety.


How to Add to Record in TypeScript

A TypeScript Record is a type definition. Once you declare a value with that type, you cannot add arbitrary keys unless the type permits them. However, if you're building up a Record-typed object dynamically, you can do it using an intermediate object and type assertion:

type FeatureFlags = Record<'darkMode' | 'betaAccess', boolean>;

const flags: Partial<FeatureFlags> = {};
flags.darkMode = true;
flags.betaAccess = false;

const finalFlags = flags as FeatureFlags;

Here, Partial allows you to construct the object incrementally. Once all keys are populated, you can assert it back to the full type.

If you need dynamic keys not known ahead of time, a Record<string, Type> or a Map may be a better fit.


Record vs Map in TypeScript

While Record and Map both associate keys with values, they serve different purposes in TypeScript.

A Record is a compile-time type definition for plain JavaScript objects. It is fully static and enforced by the TypeScript compiler. Keys must be strings or numbers known ahead of time.

A Map, by contrast, is a runtime object that can use any value (including objects) as keys and supports dynamic addition and deletion of entries.

Here’s a quick comparison:

  • Record TypeScript: Ideal for static, known-in-advance key-value structures.
  • Map: Better for dynamic or large data sets, or when key types are complex.

If you're modeling something like user roles or configurations, Record offers better performance and tooling support. But if you need runtime flexibility, a Map is more appropriate.


Transforming Union Types with Record

One of the powerful use cases for TypeScript Record is converting a union type into an object type. This pattern is useful for cases like event handler maps:

type Events = 'click' | 'hover' | 'focus';

type EventHandlers = Record<Events, () => void>;

const handlers: EventHandlers = {
  click: () => console.log('Clicked'),
  hover: () => console.log('Hovered'),
  focus: () => console.log('Focused')
};

This guarantees every event has a corresponding handler, reducing bugs from unhandled cases and improving code organization.


Working with Record and Generics

You can define generic utility types using Record. For instance, if you’re building reusable configurations or service layers, you might write:

function createLogger<T extends string>(levels: T[]): Record<T, () => void> {
  const logger = {} as Record<T, () => void>;

  levels.forEach(level => {
    logger[level] = () => console.log(`Logging at level: ${level}`);
  });

  return logger;
}

const myLogger = createLogger(['info', 'warn', 'error']);
myLogger.warn(); // Logs: Logging at level: warn

This dynamic and type-safe approach is only possible because Record can preserve the structure of generic parameters.


Practical Applications

The TypeScript Record type is especially useful in component-based development. For example, React developers often use it to map component names to their implementations:

type ComponentMap = Record<'Button' | 'Input' | 'Select', React.ComponentType<any>>;

const components: ComponentMap = {
  Button: MyButton,
  Input: MyInput,
  Select: MySelect
};

It also plays a role in internationalization, where message keys must be tightly controlled:

type MessageKeys = 'welcome' | 'logout' | 'error';
type Translations = Record<MessageKeys, string>;

const enMessages: Translations = {
  welcome: 'Welcome!',
  logout: 'Log out',
  error: 'An error occurred'
};

These structures enforce consistency and reduce the likelihood of missing or misnamed keys.


Summary

The TypeScript Record type is a powerful utility that simplifies defining and working with key-value pairs. By using Record TypeScript, developers can create strongly typed mappings from a set of known keys to consistent value types. It promotes better tooling support, reduces runtime errors, and improves readability.

Whether you're building access control matrices, component maps, event handler registries, or configuration objects, the Record type provides a clear and concise way to ensure every key is accounted for and correctly typed. Alternatives like Map offer flexibility at runtime, but for static, type-safe development, Record is a more suitable choice.

Learn TypeScript for Free
Start learning now
button icon
To advance beyond this tutorial and learn TypeScript by doing, try the interactive experience of Mimo. Whether you're starting from scratch or brushing up your coding skills, Mimo helps you take your coding journey above and beyond.

Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.

You can code, too.

© 2025 Mimo GmbH

Reach your coding goals faster