Overview
The TypeScript Record
type has the following syntax:
Record<K, T>
It can be used to construct an object type that has keys/properties of type "K
" with corresponding values of type "T
". Please note though, that the following rules apply to the type of "K
" you can specify with the Record
utility type:
- It can be a union type;
- It must be a string, number or a symbol.
Please note that in TypeScript 2.1 Record
mapped types only had support for string
keys, and any non-string values passed in as the key got coerced into a string. In TypeScript 2.9, however, support for number
and symbol
keys was added. If you want to learn more about the underlying implementation of these, please refer to the "under the hood" section.
Examples
The Record
utility type can be useful in a number of ways. In the following examples you can see some of these to have an idea about the flexibility it provides:
Basic Example:
Let's consider the following basic example that demonstrates the utility the Record
type provides:
type Keys = 'a' | 'b';
type Values = 'foo' | 'bar';
const obj: Record<Keys, Values> = {
a: 'foo',
b: 'bar',
}
Example Using String Keys With a Specific Set of Values:
To accept any number of string keys with a specific set of values, you could do something like the following:
type AcceptedValues = 'y' | 'n';
type ProductMeta = Record<string, AcceptedValues>;
const productMeta: ProductMeta = {
isSatisfied: 'y',
isReviewed: 'n',
// ...
};
Example Using Keys From an Existing Type With Generic Values:
To extract keys from an existing type and use it with, let's suppose any string value, you could do something like the following:
interface Product {
name: string,
price: number,
quantity: number,
}
type ProductApi = Record<keyof Product, string>
const product: ProductApi = {
name: 'Phone',
price: '1234',
quantity: '10',
};
Example Using Defined Set of Keys & Values
interface Dropdown {
label: string;
value: string;
}
type ProductTypes = 'tops' | 'bottoms';
type ProductFilters = Record<ProductTypes, Dropdown[]>
const sizes: ProductFilters = {
tops: [
{ label: 'size', value: 'xs' },
{ label: 'size', value: 's' },
{ label: 'size', value: 'm' },
],
bottoms: [
{ label: 'size', value: 's' },
{ label: 'size', value: 'm' },
{ label: 'size', value: 'l' },
],
};
Under the Hood
For those of you who are curious, the underlying implementation of Record<Keys, Values>
is as follows:
// TypeScript 2.9+
type Record<K extends keyof any, T> = {
[P in K]: T;
}
Instead of using the Record
utility, you could re-write, for example the basic example from earlier, as follows:
type Keys = 'a' | 'b';
type Values = 'foo' | 'bar';
const obj: { [K in Keys]: Values } = {
a: 'foo',
b: 'bar',
}
Which is basically exactly what the Record
type does as well.
As mentioned earlier, the implementation of the Record
utility type was slightly different in TypeScript v2.1, where it was implemented as follows:
// TypeScript 2.1+
type Record<K extends string, T> = {
[P in K]: T;
}
This post was published (and was last revised ) by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.