What could cause a random key to be absent from a Record<string,any>?

Consider:

// valid
let a: Record<string, any> = {info: "world"};

// Property 'info' is missing in type 'Record<string, any>' but required in type '{ info: string; }'
let b: {info: string} = a;

Doesn't the definition of a Record imply that it contains keys of type K and values of type T? Why does specifying certain keys for a record cause a compatibility issue?

I could make the property optional to resolve this:

// valid
let b: {info?: string} = a;

Shouldn't a more specific type like {stringkeyname: any} be considered compatible with Record<string, any>?

Answer №1

While the Record<K, V> utility type signifies "an object type with keys of type K and values of type V", it does not guarantee the presence of every key of type K. Implementing Record<K, V> as a mapped type {[P in K]: V}, results in a wide type like {[k: string]: V} when mapping over a key type such as string. This differs from unions of literal types like

"a" | "b" | "c"
, where
Record<"a" | "b" | "c", V>
translates to {a: V, b: V, c: V}. Let's compare an index signature type like { [k: string]: string } with an object type containing known keys like { data: string }:

declare let b: { data: string };
declare let a: { [k: string]: string };
a = b; // valid
b = a; // error
// Property 'data' is missing in type '{ [k: string]: string; }' 
// but required in type '{ data: string; }'

The scenario presented involves successful assignment of { data: string } to { [k: string]: string }, whereas the reverse fails, prompting curiosity as to why.

Explaining this goes beyond just TypeScript's consistent type system. Certain types exhibit varied behavior based on operations performed on them. Although TypeScript aims for type safety, achieving 100% consistency is not a language goal (non-goal #3). There exists a tradeoff between safety and usability.

For a thorough explanation, refer to this comment on microsoft/TypeScript#32987 and subsequent discussions.


Essentially, an index signature type like { [k: string]: V } implies "every property of the object with a key of type string has a value of type V". It does not ensure that "all possible keys of type string exist as keys of the object, each paired with a value of type

V</code". Creating a plain object with all conceivable <code>string
-valued keys is unrealistic (although feasible through a Proxy method). An index signature such as
declare const obj: {[k: string]: V}
facilitates property iteration using for (const k in obj) { obj[k] }, ensuring that obj[k] corresponds to type V. Random key indexing like obj.foobarbazqux might yield no property or result in undefined. Although --noUncheckedIndexedAccess can help by returning V | undefined, encompassing random key checks proves cumbersome during property iteration.

Hence, an index signature denotes key-value relationships without promising the existence of particular keys.


In contrast, an object type like {data: V} ensures the presence of a property with the key "data" having a value of type V. The intended use includes key-specific indexing like obj.data, rather than iterating properties via for...in loops.

Technically, {data: V} does not specify additional properties. Therefore, the object might possess various other key-value pairs apart from data, though TypeScript typically treats it as lacking extra properties after creating a type like {data: V} from an object literal, adhering to excess property checking.


Regarding mutual assignment attempts, assigning {data: V} to {[k: string]: V} is permissible. Here, {data: V} acquires an implicit index signature, allowing compatibility due to the assignability of every known string-keyed property in {data: V} to type V. Despite potential excess properties, occurrences are infrequent enough to allow this assignment.

Conversely, trying to assign {[k: string]: V} to {data: V} fails. An object type like {[k: string]: V} rarely includes the specific known keys present in an object type such as {data: V}. Although crafting an instance demonstrating compatibility is plausible (

const rec: Record<string, string> = {data: "abc"}; const obj: {data: string} = rec
), TypeScript solely recognizes the annotated type of rec and disassociates data information, treating it akin to
const rec: Record<string, string> = {oops: "abc"}; const obj: {data: string} = rec
, showcasing unsafety. Attempting to access data property following an object update to {data: string} confronts a mismatch with a Record<string, string>. As a consequence, disallowing the assignment prioritizes avoiding unsafe outcomes.


To sum up, despite inconsistencies, some scenarios challenge TypeScript's strict rules. Disabling --noUncheckedIndexedAccess enables seemingly risky operations like performing rec.foobarbazqux.toUpperCase() sans compiler warnings, although runtime errors remain likely. Balancing type safety against practicality, commonplace activities take precedence in TypeScript enforcement decisions over uncommon cases.

Playground link to code

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

After defining Partial<T>, encountering an error trying to access an undefined property is unexpected

In my function, I am attempting to standardize certain values by specifying the whole function type as Partial. However, despite declaring the interaction variable as Partial Type, I keep encountering the error message saying "Cannot read property endTime ...

Managing return types in functions based on conditions

Recently, I developed a function to map links originating from a CMS. However, there are instances where the link in the CMS is optional. In such cases, I need to return null. On the other hand, when the links are mandatory, having null as a return type is ...

Is it possible to apply Mat-card to an Anchor Element?

How can I make a mat-card clickable and show a link cursor when hovered over? I have multiple cards and when I click on one, I want to navigate to another page. What is the best way to achieve this? Is it acceptable to use the following code in my templat ...

Utilize your custom types within an npm package without the need to release them to @types

I recently came across this npm package (https://www.npmjs.com/package/react-web-notification) and decided to integrate it into my project. To do so, I created an index.d.ts file within the node_modules/@types/react-web-notification folder: import * as Re ...

Utilize regular expressions to substitute a specific string of text with an HTML tag

My task is to transform this text: let text = "#Jim, Start editing to see some magic happen!"; into this format: text = "<span style="color:red">#Jim</span>, Start editing to see some magic happen!"; Here is my ...

Incorporate JavaScript Library into StencilJs Using TypeScript

Recently, I decided to incorporate a JavaScript library called Particles.js into my project. The process involved importing it and initializing it within my component: import { Component, h } from '@stencil/core'; import * as particlesJS from &a ...

Guide on merging paths in distinct modules within an Angular project

I am looking to merge two sets of routes from different modules in my application. The first module is located at: ./app-routing.module.ts import {NgModule} from '@angular/core'; import {Routes, RouterModule} from '@angular/router'; i ...

Error: The function setEmail is not defined. (In 'setEmail(input)', 'setEmail' is not recognized) (Cannot utilize hooks with context API)

App.js const[getScreen, showScreen] = useState(false); const[getEmail, setEmail] = useState(""); <LoginScreen/> <LoginContexts.Provider value={{getEmail,setEmail,getScreen,showScreen}}> {showScreen?<Screen2/>:<LoginScre ...

Developing a React-based UI library that combines both client-side and server-side components: A step-by-step

I'm working on developing a library that will export both server components and client components. The goal is to have it compatible with the Next.js app router, but I've run into a problem. It seems like when I build the library, the client comp ...

Utilize the index of a for loop to manipulate an Angular string

When working with different objects and creating forms simultaneously, I've come across a challenge. My initial idea for handling the submission was to use the following code: <form (ngSubmit)="submitForm{{u}}()"> However, incorporating the in ...

How to Delete Multiple Rows from an Angular 4 Table

I have successfully figured out how to remove a single row from a table using splice method. Now, I am looking to extend this functionality to remove multiple rows at once. html <tr *ngFor="let member of members; let i = index;"> <td> ...

Unraveling the mysteries of webpack configuration

import * as webpack from 'webpack'; ... transforms.webpackConfiguration = (config: webpack.Configuration) => { patchWebpackConfig(config, options); While reviewing code within an Angular project, I came across the snippet above. One part ...

Sending data from the LoginComponent to the RootComponent

I am facing a challenge with implementing *ngIf to hide the login/logout option in the navbar based on the user's authentication status. When logged in, I want to hide the logout link. Here is my current setup. app.component.ts import { Component, O ...

I am in need of a customized 'container' template that will display MyComponent based on a specific condition known as 'externalCondition'. MyComponent includes the usage of a Form and formValidation functionalities

container.html <div ngIf="externalCondition"> <!--Initially this is false. Later became true --!> <my-component #MyComponentElem > </my-component> <button [disabled]= "!myComponentElemRef.myDetailsF ...

What steps are needed to develop a TypeScript component within Angular framework?

I've been attempting to develop an Angular Component in TypeScript. I'm trying to utilize document.createElement to build a toolbar within my component, but it's not appearing. Below is my Component code: import {Directive, Component, boot ...

Tips for expanding a React class using typescript

Let's consider a basic example: interface BaseProps { name: string; } class BaseClass<P extends BaseProps> extends React.Component<P, void> { } interface SuperProps { } class SuperClass extends BaseClass<SuperProps> { } M ...

Importing Typescript modules by specifying their namespace instead of using a function

I have been working on a project where I needed to generate typings from graphql using the gql2ts library. In the gql-2-ts file, I initially used a namespace import for glob, which resulted in TypeScript showing me an error as intended. I then switched the ...

Angular 5: Issue with Module Resolution: Unable to locate '@angular/forms/src/validators'

Struggling to develop a unique validator using a directive, but encountering the following error: ERROR in ./src/app/CustomValidators/white-space-validator.directive.ts Module not found: Error: Can't resolve '@angular/forms/src/validators' ...

Issue with Angular 6 auth guard causing logged-in users to remain on a blank page

I came across this answer and am tweaking it to work with my authentication system: Angular 6 AuthGuard However, I'm facing an issue where after successful authentication, instead of redirecting to the specified URL, the auth guard leads to a blank p ...

The property 1 cannot be added because the object is not extendable in React

Does anyone know what is causing the following issue? I am unable to insert a new object into a key object within my arrays of objects. For example, when I try to insert a new email at index 1 in the 'emails' array, it throws an error stating "ca ...