What Is the TypeScript Record Type?

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:

  1. It can be a union type;
  2. 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.