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

How to Animate the Deletion of an Angular Component in Motion?

This stackblitz demonstration showcases an animation where clicking on Create success causes the components view to smoothly transition from opacity 0 to opacity 1 over a duration of 5 seconds. If we clear the container using this.container.clear(), the r ...

Indicate the type of content returned by a Controller

I'm dealing with a metrics.controller.ts file that looks like this: import { Controller, Get } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiUseTags, ApiModelProperty } from '@nestjs/swagger'; import { PrometheusSe ...

Exploring the power of global injectors in Angular 7 for component inheritance

Recently, I have been following a method outlined in a blog post on MSDN to simplify the extension of components without having to include all dependencies in the super() call. However, this approach seems to have ceased working in Angular 7 with Typescrip ...

Converting TypeScript to JavaScript: A Step-by-Step Guide

I have this code written in Typescript and I need to convert it to JavaScript const Home = (props) => { return ( <div> {props.name ? 'Hi ' + props.name : 'You are not logged in'} </div> ); }; How can I re ...

What is the best way to preserve an enumeration value in TypeScript?

Is there a way to save enumeration values in TypeScript? For instance: createArticle(name: string, clr: ??enumeration??) { return axios.post(`${environment.apiUrl}/cards`, { card: `${name}`, color: ??clr?? }, ... } PS: Conte ...

Implicated Generic in TypeScript

Is there a way to simplify my class A implementation? export class A<TB extends B<TC>, TC> implements TD<TB, TC> { make(): TC {} } Currently, I have to specify the TC type every time I create an instance of A: class CTest {} class BTes ...

Exploring the world of mocking tests using Jest and inputs

Is there a way to create a jest test specifically for this function? const input = require('prompt-sync')(); export function choices(): void { const choice = input("Choose a letter"); if (choice === "a") { con ...

Error when attempting to add data into MongoDB using Node.JS: "The type 'string' cannot be assigned to type 'ObjectId | undefined'."

Attempting to add a document to the collection results in an error when specifying the _id field of the added document. How can I insert a document with an _id that is not an ObjectId? The error occurs with the following code. Omitting the _id resolves th ...

Node.js and Express: The error message "Cors is not a function"

Everything was running smoothly until this morning when out of nowhere, a type error popped up stating that Cors is not a function Here's my code: import * as Cors from "cors"; ... const corsOptions: Cors.CorsOptions = { allowedHeaders: ["Origi ...

Ways to address the Generic Object Injection Sink eslint error (security/detect-object-injection)

I am seeking a solution to resolve this issue without needing to deactivate eslint. Moreover, I am eager to comprehend the cause of the error. const getMappedCard = (cardName: CardName) => { const mappedCards = { Mastercard: <Mastercard /> ...

Defining a custom type for accessing Date.getTime() in TypeScript

Are there any data types similar to Timestamp that could be utilized for Date().getTime() purposes? const currentTime = new Date().getTime(); ...

Can you explain the distinction between declaring type using the colon versus the as syntax?

Can you explain the variation between using the : syntax for declaring type let serverMessage: UServerMessage = message; and the as syntax? let serverMessage = message as UServerMessage; It appears that they yield identical outcomes in this particular ...

Modify the MUI time picker to display as a digital clock inside a DateTimePicker widget

I need to update my MUI DateTimePicker component to use the DigitalClock time picker instead of the Analog Clock picker. The current component has two steps, first picking the date from a calendar and then selecting the time. This change is only necessary ...

Error message stating 'Module not found' is displaying in the browser console

As a beginner with Angular CLI, I recently executed the following commands at the root of my Angular project. issue-management\src\webui>ng generate module pages\dashboard issue-management\src\webui>ng generate component pag ...

selective ancestor label Angular 8

I am looking for a way to place my content within a different tag based on a specific condition. For instance, I need my content to be enclosed in either a <table> or <div> depending on the condition. <table|div class="someClass" ...

Visual Studio - TypeScript project synchronization issue

Currently using the 2015 version of Visual Studio Community, I am facing an issue while working on a typescript project. Whenever I make modifications to the code, debug it, and save it using ctrl + s followed by refreshing the browser with ctrl + r, the c ...

Creating a build task in Visual Studio Code with universal TypeScript compiler settings

Our project has the following structure: +-- views +-- viewXXX +-- ts ¦ +-- controller.ts ¦ +-- helper.ts ¦ +-- ... (*.ts) +-- viewXXX.ctrl.js // this is the desired output file +-- viewXXX.c ...

Utilizing the `in` operator for type narrowing is yielding unexpected results

Attempting to narrow down a type in TypeScript: const result = await fetch('example.com') if (typeof result === "object" && "errors" in result) { console.error(result.errors); } To clarify, the type of result before the if condition should be ...

The @ViewChild in Angular 2 seems to be unable to detect the BaseChartDirective from the ng2-charts

I'm currently using ng2-charts v1.5.0 and I'm facing an issue with updating the chart data after a click event. Despite following suggestions from other sources, I am unable to get it to work properly. Here is a snippet of my code: <div styl ...

When using Angular 2, an error may occur where you receive a message stating that you cannot read the property 'length' of undefined while attempting to call

When creating a component (let's call it A) with the @input decorator to retrieve values from the selector, keep in mind that this component will generate text fields based on the input values specified in the selector. Component A is then utilized in ...