Accessing properties for objects with map-like characteristics

Many interfaces allow for arbitrary data, as shown below:

interface Something {
  name: string;
  data: { [key: string]: any };
}

The problem arises when trying to access or set values on objects with arbitrary keys in Typescript.

let a: Something = {
  name: 'foo',
  data: { bar: 123 }
};

// Results in error message: 'Property "bar" does not exist on type {[key:string]: any}'
console.log(a.data.bar);
a.data.bar = 234;

Is this an oversight in Typescript's functionality or is there a workaround to prevent these errors?

This example in the Typescript playground showcases the issue clearly.

Edit

I am seeking a solution that doesn't require a complete rewrite of the codebase from a.data.bar to a.data['bar'].

Answer №1

If you specify the type as indexable, then you must access the properties in this manner:

a["data"].bar

Your playground demo: adjusted to function correctly.

Learn more about Indexable Types.


Let's begin with the issue at hand:

interface Something {
    name: string;
    data: { [key: string]: any };
}

In your Something interface, there is a specific property named name of type string, so the compiler understands what this signifies:

let a: Something = ...
console.log(a.name);

However, if I were to execute console.log(a["unknownKey"]), how could the compiler ascertain the meaning of this unknownKey? Is it a valid property within the object? What is its type?
Since you haven't specified that this object contains this key, the compiler cannot deduce, hence necessitating the usage of index notation.

How can we work around this?
One approach is to define the properties that are known to exist and used in your code. For instance, if you utilize the properties name, id, and address, include them in your interface like so:

interface Something {
    id: string;
    name: string;
    address: string;
    data: { [key: string]: any };
}

Other properties can remain as indexes.

An alternative is to employ classes:

interface Something {
    name: string;
    data: { [key: string]: any };
}

class MySomething {
    public name: string;
    public else: string;

    constructor(obj: Something) {
        this.name = obj.name;
        this.else = obj["else"];
    }
}

Of course, utilizing any is another option, bypassing the compiler's type checking:

let a: any = ...
console.log(a.myProperty);

This is not an ideal solution, but temporary implementation for resolving hurdles during migration to TypeScript until a better fit is determined to suit your requirements.

Answer №2

Here are some choices you have:

(a as any).data.bar
(a.data as any).bar

Alternatively, you have the option to redefine the Something interface as follows:

interface Something {
  title: string;
  info: any;
}

All of these options do involve sacrificing a certain level of type checking.

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

Managing updates with the spread syntax: Dealing with undefined or null properties

Let's take a look at this example method: GetCustomerWithPoints(customerId: number): Customer { const customer = this.customerService.getCustomer(customerId); const points = this.pointService.getPointsForCustomer(customerId); return {...custo ...

ts-node: The colon symbol was not expected in this context

As I work on developing a backend server for my application, I made the decision to switch from using babel-node as the executor to utilizing ts-node. The command defined in my package.json file is: "server": "cd server && ts-node --project tsconf ...

Tips for removing a row from a DataGrid column with the click of a button

I am facing a challenge with my data table that contains users. I am trying to implement a delete button for each row, but it seems like the traditional React approach may not work in this situation. Currently, I am utilizing the DataGrid component in the ...

The TypeScript factory class anticipates an intersection

In my code, I have a class factory called pickSomething that generates a specific type based on a key provided from a ClassMap: class A { keya = "a" as const; } class B { keyb = "b" as const; } type ClassMap = { a: A b: B } c ...

What is the process of implementing a particular FormControl from a FormArray in my HTML file?

My FormArray initialization code is as follows: this.contents.forEach(content=> { this.formArray.push( new FormControl(content.text, Validators.required)); }); Now, I am trying to associate a specific FormControl with my textarea by using i ...

Encountering an issue with the 'createObjectURL' function in URL, resulting in overload resolution failure when using npm file-saver

While working on my angular app, I encountered a situation where I needed to download user details uploaded as a Word document to my local machine using the angular app. Successfully, I was able to upload and save this data to my database, getting its byte ...

Issue with TypeScript Declaration File in NPM module functionality

Recently, I've been working on developing a package for NPM. It's essentially a JSON wrapped database concept, and it has been quite an enjoyable project so far. However, I've been facing some challenges when trying to include declarations f ...

Explaining the data link between a service and component: understanding Subject and BehaviorSubject

Can someone explain the concepts of Subject and BehaviorSubject in Angular to me? I'm new to Angular and struggling to understand. I've tried searching online, but I still can't grasp how they work. The same goes for Observable and Observer ...

What is a Mongoose Schema type in TypeScript and how can it be used as a custom

https://i.stack.imgur.com/mtlRi.png Could anyone assist me with storing a custom object that includes attributes from the StationRating interface? ...

Derive a data type from a parameter passed to a function defined within an interface

I am working with a function defined in an interface as shown below: interface UseAutocompleteReturnValue { ... getRootProps: (externalProps?: any) => React.HTMLAttributes<HTMLDivElement>; } Within my interface, I aim to create a prop named rootP ...

I've been stuck for hours, is there anything I should include?

I'm attempting to access http://localhost:4200/Personnes/view/:2, but I encountered the following error (ERROR TypeError: Cannot read property 'nom' of undefined) "My personnnes.service.component.ts" `export class PersonnesService { baseUr ...

Troubleshooting Date Errors in Typescript with VueJS

Encountering a peculiar issue with Typescript while attempting to instantiate a new Date object. <template> <div> Testing Date</div> </template> <script lang="ts"> import Vue from "vue"; export default Vue.extend({ name: ...

Tips for preserving updates following modifications in Angular 5/6?

I'm currently working on enhancing the account information page by implementing a feature that allows users to edit and save their details, such as their name. However, I am encountering an issue where the old name persists after making changes. Below ...

Incorrect tsx date interpretation when dealing with years such as 0022

I am facing an issue with dates in tsx. The problem lies in the fact that when I set a date like 30/11/0022, it interprets the date as 30/11/1922, which is incorrect. Here is the input element I have in tsx: <FormikField name="Birthdate" disa ...

What could be causing the malfunction of getter/setter in a Vue TypeScript class component?

Recently delving into the world of vue.js, I find myself puzzled by the unexpected behavior of the code snippet below: <template> <page-layout> <h1>Hello, Invoicer here</h1> <form class="invoicer-form"> ...

Enhancing RxJS arrays of Observables with supplementary data for preservation

Question: Processing Array of Observables with Metadata in Angular How can I process an array of Observables, such as using forkJoin, while passing additional metadata for each Observable to be used in the pipe and map functions? const source = {animal: & ...

Encountering difficulty invoking a component method from d3's call() function

My current setup involves using D3 to drag and drop links in the following manner: .call(d3.drag() .on("start", linkDragStart) .on("drag", linkDragging) .on("end", linkDragEnd)); Recently, I decided to extract this functionality into a separate met ...

Find the object in the array that has a name that is a combination of

I am facing an issue in implementing TypeScript validation for filtering an array. I have a specific array of actions and I want to filter out internal actions from it. Despite my efforts, I am unable to properly communicate to TypeScript that the filtered ...

Using TypeScript to filter and compare two arrays based on a specific condition

Can someone help me with filtering certain attributes using another array? If a condition is met, I would like to return other attributes. Here's an example: Array1 = [{offenceCode: 'JLN14', offenceDesc:'Speeding'}] Array2 = [{id ...

What is the process for utilizing a Typescript Unit Test to test Typescript code within Visual Studio?

Currently, I am facing an issue while writing a unit test in Typescript to check a Typescript class. The problem arises when the test is executed as it is unable to recognize the class. To provide some context, my setup includes Typescript (1.4) with Node ...