What is the reason for Record accepting an array in TypeScript?

I'm puzzled as to why this code compiles in typescript. I've tried researching and checking the typescript documentation, but still can't find the answer.

type RecType = Record<string, any>
const arr: RecType = [1, 2, "three"] //or new Array(1, 2, 3)

console.log(arr)  // [1, 2, "three"] 
console.log(Array.isArray(arr)) // true
console.log(Object.keys(arr)) // ["0", "1", "2"] 

If you want to experiment with the code, here is a typescript playground link

Answer №1

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

Answer №2

After taking a short break, returning to the issue and delving a bit deeper, I believe I have grasped it.

This is the definition of Record in typescript (taken from its source code)

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

An array can be assigned to this because

  • The keyof operator will return the array's indices as keys (similar to how Object.keys functions).
  • The accessor [P in K]: T is applicable for an array because array["0"] is a valid way to access index 0 of the array (as well as array[0])

I believe my understanding is correct now, but please feel free to point out any errors or provide corrections.

Answer №3

In order to create a string keyed object with unknown values while preventing arrays from being included, it is recommended to utilize Record<string, unknown>. This ensures that only non-array values can be passed in. For a hands-on example, check out this TS Playground link.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Develop a TypeScript project in React that incorporates a dynamic country flag component

Hi there! I've been diving into the world of using React with TypeScript recently, and I came across a package called "react country flag" that I wanted to use for displaying country logos. However, I'm running into issues trying to use it with T ...

Getting the ID from an array of objects in AngularJS: A How-To Guide

I need help with extracting the id from an array of objects in AngularJS. It seems like $scope.data.id is returning undefined. Can anyone spot what I may be doing wrong, or suggest a better way to achieve this? Data: [{"_id":"57e540ab352e81329c984aba","n ...

Encountering a sign-in issue with credentials in next-auth. The credential authorization process is resulting in a

I am currently facing an issue with deploying my Next.js project on Vercel. While the login functionality works perfectly in a development environment, I encounter difficulties when trying to sign in with credentials in Production, receiving a 401 error st ...

Tips for adjusting HighCharts layout with highcharts-vue integrations

I have a fairly simple component: <template> <div> <chart v-if="!loading" ref="priceGraph" constructor-type="stockChart" :options="chartData" ...

Unforeseen outcomes when duplicating array elements in C

I have been attempting to replicate elements of an array by using a basic copy function located outside of the main function. The code I have written is as follows: int* copy(int* nums){ int size = sizeof(nums) / sizeof(nums[0]); // determining array l ...

Are there any comparable features in Angular 8 to Angular 1's $filter('orderBy') function?

Just starting out with Angular and curious about the alternative for $filter('orderBy') that is used in an AngularJS controller. AngularJS example: $scope.itemsSorted = $filter('orderBy')($scope.newFilteredData, 'page_index&apos ...

Unable to add value to array within PHP foreach loop

Recently, I encountered an issue with a foreach statement in my code. I attempted to add an extra value to the array within the loop, and it appeared to be working fine initially. However, all of a sudden, the added values were no longer present when I ran ...

Effectively retrieving text from a sentence

I've been struggling with this issue for quite some time now and I'm hopeful that I can find a solution soon. Within a sentence, there is crucial information that I need to extract. After extracting these strings, my plan is to store them in an ...

I'm wondering how to convert JSON to an array list using Python. Can anyone help

Help needed with organizing JSON data into a nested dictionary using Python. The goal is to create a treeview with parent/child relationships by looping through the variable recursively. I need assistance in creating a structured array or list from the sa ...

A guide to seamlessly uploading files to s3 using nextjs, node, and typescript

I've been struggling to successfully upload a basic image to s3 using ts/nextjs/node. Despite having all the necessary credentials and code in place, I'm still unable to get it working. Can someone please provide clear instructions on how to achi ...

Searching for a contiguous subarray with a specific sum in linear time complexity (O(n))

After solving the question, I realized that the time complexity of my code is O(n^2). The code includes two loops, leading to the O(n^2) time complexity. Is there a way I can modify my code to achieve the same solution in O(n) time instead? import java. ...

Breaking down a string into an array using Node.js

Is there a way to change the following text into an array using nodejs? "Content: [{id: 1, value: 1, number: 1},{id: 13, value: 1, number: 3},{id: 14, value: 1, number: 3},]" I attempted to use JSON.parse, but encountered this error: Unexpected token c i ...

Unleashing the Power of Firebase Service in Angular Components: A Guide to Effective Unit Testing

I am currently working on testing my Create-User-Component which relies on an Auth Service that includes methods like 'login, logout,' etc. The Auth Service imports both AngularFireAuth and AngularFirestore, and it is responsible for handling da ...

Updating parent object data in child: A step-by-step guide

Currently, I am in the process of creating a custom dropdown component. Within this component, I have a config object that is initialized within the ngOnInit() method. This config object combines both default configurations as well as user-provided configu ...

Troubleshooting the Issue with Angular Material Dialog Imports

Hey there, I'm trying to utilize the Angular Material dialog, but I'm encountering issues with the imports and I can't seem to figure out what's wrong. I have an Angular Material module where I imported MatDialog, and I made sure to i ...

Differences between Arrays of Numbers and Objects in Typegoose

Exploring Typegoose and encountering a puzzling issue. I defined a class with a field meant to store an array of numbers like this: class A { ... some other properties ... @prop({ required: true }) public nums!: number[]; } Here's a snippet of ...

Printing an array that contains multiple nested arrays efficiently with a single Foreach loop

During a recent interview, I faced numerous questions but stumbled on one particular inquiry: The task was to print a multi-dimensional array using only one foreach loop. $cars = array ( array("Volvo",22,18), array("BMW",15,13), array("Saa ...

Encountering TypeScript errors while trying to implement Headless UI documentation

import { forwardRef } from 'react' import Link from 'next/link' import { Menu } from '@headlessui/react' const MyLink = forwardRef((props, ref) => { let { href, children, ...rest } = props return ( <Link href={href}&g ...

JavaScript Equivalent to C#'s BinaryReader.ReadString() Function

Currently, I am in the process of translating C# code into JavaScript. While transferring multiple datatypes from this file to matching functionalities found in various JavaScript libraries was relatively smooth, there is one specific function that seems t ...

Typescript question: What is the specific error type associated with the 'throw' function in express-validator?

I am trying to identify the type of error thrown by this function: validationResult(req).throw() This is how the throw function is defined: throw() { if (!this.isEmpty()) { throw Object.assign(new Error(), utils_1.bindAll(this)); } } Here ...