The concept of Record
can be defined as follows:
/**
* Create a type that consists of properties K of type T
*/
type Record<K extends keyof any, T> = {
[P in K]: T;
};
For example, when defining a type like
type MyType = Record<string, string>;
, the inline version of
Record
results in the following type:
type MyType = {
[P in string]: string;
};
This essentially instructs to generate an object type with string-based property names within the set string
. Since string
is not limited, there is no restriction on the number or names of properties (unlike using a union of string literal types like "prop1" | "prop2"
), indicating that the object can have any number of properties with any name, as long as they are of type string
.
Hence, in terms of type checking, it is similar to having an index signature without a mapped type ({ [index: string]: string; }
).
Instead of Using Record
While using Record
in this manner might seem odd and unclear to some, a more conventional approach to convey the same idea for objects with variable properties could be achieved by utilizing a plain index signature:
type ObjectWithStringProperties = {
[index: string]: string;
};
Furthermore, this method helps clarify the intended key structure. For instance:
type PersonsByName = {
[name: string]: Person;
};
const collection: PersonsByName = {};
By specifying the key name in this manner, users interacting with objects of this type will have additional context available while working in their editor.
Proper Usage of Record
It's worth noting that Record
is commonly used in scenarios like the following:
type ThreeStringProps = Record<"prop1" | "prop2" | "prop3", string>;
// translates to...
type ThreeStringProps = { [P in "prop1" | "prop2" | "prop3"]: string; };
// concludes to...
type ThreeStringProps = {
prop1: string;
prop2: string;
prop3: string;
};