Update dynamic properties by iterating and modifying their values

I recently encountered a challenge where I needed to iterate through unknown properties of an object created from a class. Fortunately, I came across a helpful resource on a webpage that provided some insight.

Initially, my code worked perfectly without any additional methods:

export class TranslateEntry {
    datum: string = 'Datum';
    sport: string = 'Sport';
    leistung: string = 'Leistung';
    einheit: string = 'Einheit';
    constructor()
    {
        type ObjectKey = keyof TranslateEntry;
        for( let key in this )
        {      
          this[ key as ObjectKey ] = "T" + this[ key as ObjectKey ] + "T";
        }
    }
}

However, when I added a method to the class, the code stopped functioning:

export class TranslateEntry {    
    datum: string = 'Datum';
    sport: string = 'Sport';
    leistung: string = 'Leistung';
    einheit: string = 'Einheit';
    constructor()
    {
        type ObjectKey = keyof TranslateEntry;
        for( let key in this )
        {      
          this[ key as ObjectKey ] = "T" + this[ key as ObjectKey ] + "T";
        }
    }

    test()
    {
        
    }
}
Type 'string' is not assignable to type 'string & (() => void)'.
  Type 'string' is not assignable to type '() => void'

Although I understand the concept of union of literal types, explained in this insightful post: What does "keyof typeof" mean in TypeScript?, I have been struggling to resolve this issue.

Any guidance or suggestions would be greatly appreciated.

Answer №1

The issue at hand is the current limitation of the type system in representing property ownership and enumerability. The keyof operator successfully retrieves all keys of a type, as demonstrated (refer to microsoft/TypeScript#18133). This means that using keyof TranslateEntry will include properties like "test"... but there's no built-in ownKeyof operator capable of differentiating between own properties such as "datum" and inherited properties like "test". While there has been a persistent feature request on microsoft/TypeScript#9726 to introduce this distinction, it has not been incorporated into the language yet (and may not be unless there is significant community interest).

This situation requires finding a workaround. Given that object types are not sealed by default, methods like for (let k in obj) {} and Object.keys(obj) do not always return only the known TypeScript keys (see TypeScript: Object.keys return string[] and Define key type for object in `for-in`). This necessitates utilizing type assertion with statements like key as ObjectKey. Thus, extending the workaround further might involve explicitly listing out properties like

key as "datum" | "sport" | "leistung" | "einheit"
.

In cases where manual listing is cumbersome and assuming that all function-valued properties are inherited, one can customize ObjectKey to exclude such keys through modification:

Modify TranslateEntry class definition...

Rename ObjectKey to NonFunctionProp, highlighting the intent behind the adjustment. By leveraging key remapping within mapped types to filter out property keys of type assignable to Function, we automatically arrive at the union

"datum" | "sport" | "leistung" | "einheit"
.

Running this revised code reveals successful compilation without errors.


It's crucial to recognize that these assertions rely on certain assumptions about future behavior. For instance, adding a function-valued instance property to your class could lead to unforeseen issues:

Add function-valued instance property to TranslateEntry class definition...

The code continues to compile error-free even after the addition, but unexpected problems may arise when interacting with these properties later on:

Illustrative usage scenario...

By unintentionally converting a function into a string, runtime errors might occur due to unanticipated behavior. While such situations may be rare, being aware of underlying assumptions turns potential risks into calculated decisions rather than surprises.

Access the Playground link for this code snippet.

Answer №2

It's interesting how different approaches can lead to innovative solutions. I must admit, your problem statement is quite impressive and unique - something I may not have come up with myself.

TypeScript has a feature where it automatically assigns types to variables and functions, even if you don't explicitly specify them.

For example, it recognizes variables as 'string' and functions that don't return anything as 'void'.


Upon reflection, I suggest trying out arrow functions.

Instead of writing test(){}, consider using test: any = () => {}.

I believe this approach will work, but please let me know if it does not.

You can also eliminate the 'T' prefix and suffix in that string if they are unnecessary.

This implementation involves 4 strings and 1 function (test) within an object defined in SerService.

Check out the code implementation in SerService here.

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

Securing Email and Password Data in Cypress Tests

Greetings! I trust everyone is in good spirits. My dilemma lies in the fact that I am hesitant to include email and passwords in version control. I am considering using environment variables in my cypress tests and utilizing secrets for runtime value pro ...

Next JS build not displaying Typescript errors during npm build

I've been in the process of converting a Next JS project from JavaScript to TypeScript. Intentionally making mistakes to test the type checking, for example: export class Knot { position: Vector2; locked: number; deletable: boolean; isLast: bo ...

Is there a way to incorporate time into the x-axis of a ColumnRange Chart in HighChart?

I am currently using Angular to display highcharts. I am trying to show a columnRange chart based on time and user name, but I am facing difficulties. The time data I receive through API is in string format like '07:34:25' and directly displayin ...

Transitioning to a mixed Angular and AngularJS application: encountering TypeScript and AngularJS module import issues

Our team is currently working on migrating an AngularJS application to Angular, using a gradual approach. Our goal is to create new components in Angular and upgrade existing AngularJS components one by one while ensuring that the application remains funct ...

Tips for transferring data between functions in component.ts in Angular 2

When I click a button, I am passing some values using "data.someid" and I want to retrieve this value in another button click. html --- <button (click)="click1(data.someid)"></button> <button (click)="click2()"></button> In my com ...

Encountering Failures with 'npm install' on Specific Repositories

I recently acquired a bundle of Angular/Bootstrap4 templates from a source link to experiment with. However, I am encountering difficulties with the npm install step. After downloading the source and extracting the angular2seed project, I attempted the npm ...

What could be causing the lack of updates to my component in this todo list?

Based on my understanding, invoking an action in MobX should trigger a rerender for the observer. However, when I call the handleSubmit method in my AddTask component, it doesn't cause the TaskList observer to rerender. Should I also wrap AddTask in a ...

What is the best way to ensure that each service call to my controller is completed before proceeding to the next one within a loop in Angular?

Calling an Angular service can be done like this: this.webService.add(id) .subscribe(result => { // perform required actions }, error => { // handle errors }); // Service Definition add(id: number): Observable < any > { retu ...

ngOnChanges does not trigger after a change in the input property

Currently, I am utilizing an Angular @Input property to assign boolean values to the child component. It seems as though 'Inside ngOnChanges' is only being printed twice every 2 seconds and then change detection ceases to occur. Could someone ple ...

Defining a structure for an entity in which its attributes distinguish between different data types and an array combination

I strongly believe that the best way to showcase this concept is through a clear example: enum EventType { A, B, C }; type MyEvent = [EventType.A, number] | [EventType.B, string] | [EventType.C, number, string]; const eventsGrouped: Record<Event ...

Learn how to bring a component into another component within Angular

I have developed a component named CopySchedulefromSiteComponent and now I am looking to import it into another component called SiteScheduleComponent. However, I am unsure of the correct way to do this. The CopySchedulefromSiteComponent contains one fiel ...

Assigning values to object properties in a TypeScript loop

I need to update a Feature object in my code. Here is a simplified version of the structure: type Feature = {id: number, name: string, deletedAt?: Date} const newData: Partial<Feature> & {id: number} = { id: 1, deletedAt: new Date() }; const ...

Display nested components without the need for a parent container element - Divide SVG elements into individual components

I am currently working on splitting an SVG element within a component into separate parts and creating nested components dynamically from the parent component. This is necessary as I am constructing the entire SVG graphic based on a json file that dictates ...

Tips for obtaining the Component instance from the ViewContainerRef.get() method for dynamically created components

Explanation of my current goal successfully add components to a ViewContainerRef (completed) initialize these dynamic components with properties (done) gain access to the dynamically created Component instances for decision-making purposes. The issue at ...

Is there a way to automatically re-run Angular unit tests when changes are made, perhaps through the

Apologies, I am having trouble figuring out how to phrase or search for this. Are you familiar with the concept of having unit tests running in a console window and automatically rerunning whenever changes are made and saved? I am interested in adding a ...

Dynamically loading external JavaScript for an Angular component and triggering the window load event

I am currently dealing with an external javascript file that I only want to be included on a specific component, so the approach I'm taking involves dynamically loading it. I came across this answer that explains exactly how to achieve this. The prob ...

Encountering difficulties transferring data from a parent to a child component within Angular 9

In my current situation, I am passing a variable's value up to its grand-parent component using EventEmitter successfully. However, once the value reaches the grand-parent, I want to pass it to another child component within that grand-parent. I have ...

Unable to modify state upon submission of a form

I am facing an issue where I need to update the state on the main page, but the values are updated three levels down... This particular component is responsible for fetching all the cities, storing the data in the State, and then mapping through the citie ...

What is the best way to conceal a specific list item in an Angular 2 template?

I need help figuring out how to hide a selected list item that has been added to another list. I want the added item to be hidden from view in the original list. <div class="countries-list"> <span class="countries-list__header">{{'G ...

Is there a way to stop Element.focus() from affecting the cursor position in a contenteditable div?

When I trigger the focus function in my ReactJS project using Typescript, it moves the cursor to the beginning of the editable div. I want the cursor to remain where it is originally positioned. How can I achieve this? const element = document.getElementB ...