How does the TypeScript Record type work?

Tim Mouskhelichvili
Tim Mouskhelichvili
4 minutes to read

The TypeScript Record<Keys, Type> utility type is used to construct a new type whose properties are Keys and values are Type.

The Record type helps you create a new TypeScript type from a union type.

In this article, you will learn everything to know about the Record type, as well as get answers to the most common questions about this utility type.

What is the Record type?

The Record<K, T> TypeScript type is used to simplify type generation. It generates a new object type, with keys from the union type K. The value of each key will implement the interface T.

Here is an example of how the Record type works in practice.

typescripttype Status = 'error' | 'success';

const statusImages: Record<Status, string> = {
  error: 'image1.png',
  success: 'image2.png'
};

In the above example, we create a new object from the union type Status.

The Record type was released with TypeScript's version 2.1.

It also works with enums!

typescriptenum Status {
  'error' = 'error',
  'success' = 'success',
}

const statusImages: Record<Status, string> = {
  error: 'image1.png',
  success: 'image2.png',
};

The Record type is only working with enums in TypeScript version 2.9 and above.

The main advantage of using the Record type is the TypeScript strong type safety that you get.

If, for example, you change a value in the union type Status you will get an error in the object statusImages.

If, for example, we want to access a property in the object statusImages that is not declared in the union type Status, we will get a compiler error from TypeScript.

typescript error

The Record type will help you prevent refactoring bugs and other types of bugs.

What is a real-life use case example of the Record type?

The Record type is useful when you need to create an object with properties from a union type (or from an enum) AND when you need the value of each key to be of the same type.

For example, in your application, you want to create an object containing roles information from a union type that contains all the user roles. This can easily be achieved with the Record type.

typescriptinterface RoleInfo {
  image: string;
  icon: string;
}

type Role = 'admin' | 'user';

const roleInfo: Record<Role, RoleInfo> = {
  admin: {
    image: 'image1.png',
    icon: 'icon1.svg'
  },
  user: {
    image: 'image2.png',
    icon: 'icon2.svg'
  }
};

As you can see, we have successfully created a strongly typed object with information for each of the roles defined in the union type.

Have you noticed that I am using an interface with a type... Do you know why? I have written an extensive article about the differences between a type vs an interface, that will explain why.

What is the difference between a Record and a Map?

Some developers, confuse the Record type with the Map. Those two are NOT the same.

The Map is a JavaScript native ES6 data structure.

The Record is a representation (a type definition) that describes an object.

Read more: How to create a map in TypeScript?

What is the difference between a Record and an object?

The main difference between the object TypeScript type and the Record type is the abstraction of their keys.

You will be able to access any property of a Record<any, any>, without getting any errors.

However, you will NOT be able to access any properties of the object, because the keys are not of type any.

typescriptconst obj1: Record<any, any> = {};
obj1.prop1; // This works.

const obj2: object = {};
obj2.prop1; // This will trigger an error.

Difference between a Record and an index signature?

Both the Record type and the index signature can be used to achieve the same goal. Which one you use, is mainly a preference of style.

However, if you need a specific rule to follow, I recommend:

  1. Use the Record type when you need to use the values of a union type or an enum as properties.
  2. Use the index signature on generic objects with key names that you don't know in advance.
typescripttype Roles = 'admin' | 'manager';

const rolesImages: Record<Roles, string> = {
  admin: 'image1.png',
  manager: 'image2.png',
};

const genericImages: { [key: string]: string } = {
  admin: 'image1.png',
  role3: 'image3.png',
  role4: 'image3.png',
};
typescript record

How to iterate over a Record?

In order to iterate over a Record type object, you can use the for loop like this.

typescripttype Roles = 'admin' | 'manager';

const rolesImages: Record<Roles, string> = {
  admin: 'image1.png',
  manager: 'image2.png',
};

for (const role in rolesImages) {
  console.log(role);
}

Read more here: "How does the for loop work in TypeScript?"

Conclusion

In conclusion, the Record TypeScript type is a nice way to simplify type generation from a union type.

Thank you for reading this article, please share it with your coworkers.

Comments (2)
Reply to:
Degibons November 10, 2023 10:49AM
Really helpful!
REPLY
Saeed October 25, 2022 00:44AM
I have learned a lot.
REPLY