Surprising as it may seem, the Record
type in TypeScript is not limited to just arrays and objects. In fact, you can use it with various types of assignments:
const a: Record<string, any> = [];
const b: Record<string, any> = {};
const c: Record<string, any> = () => {};
const d: Record<string, any> = new Map();
const e: Record<string, any> = new Date();
const f: Record<string, any> = new Boolean();
const g: Record<string, any> = new String();
const h: Record<string, any> = class Klass{};
When using the new
keyword with a constructor, the instanceof Object
will return true for all cases:
function instanceOfObject(arg: Record<string, any>) {
return arg instanceof Object;
}
// All results are true
console.log(
instanceOfObject([]),
instanceOfObject({}),
instanceOfObject(() => {}),
instanceOfObject(new Map()),
instanceOfObject(new Date()),
instanceOfObject(new Boolean()),
instanceOfObject(new String()),
instanceOfObject(class Klass{}),
)
Therefore, the Record
acts as an alias for { [P in string]: any; }
, representing an object with a string index signature. This allows the instanceOfObject
function to accept any kind of objects or instances.
Remember that TypeScript follows structural typing, meaning it checks the interface of objects and looks for specific signatures within them. The core Object
interface, referenced here, includes the required string signature.
A more apt name for Record
could be Indexed
to avoid confusion with dictionaries common in other languages but absent in JavaScript.
For instance, arrays can be indexed by numeric values, showcasing:
const arr: Record<number, unknown> = [1, 2, 3]; // Valid assignment
However, there are some surprises in store:
const arr: Record<number, unknown> = "hello"; // Also works!
This behavior stems from the fact that strings are indexable by numbers, as documented here. Literal types are automatically boxed in JavaScript, leading to failed instanceof
checks when using constructors like String()
.
console.log("str" instanceof String); // Returns false
console.log(new String("str") instanceof String); // Returns true
That's why passing a string literal like "hello"
to the previous function would result in an error:
// Argument of type 'string' is not assignable to parameter of type
// 'Record<string, any>'
instanceOfObject("hello"); // Error
To distinguish between object literals and other instances, use Record<string, unknown>
, which disallows objects like arrays and the ones mentioned above lacking the necessary string index signature:
// Attempting to assign an array to a type expecting object literals
// Results in a type error due to missing string index signature
const obj: Record<string, unknown> = [1];
Explore further on TS Playground:TS Playground