Add an additional boolean attribute called `_ro` as a suffix to a property

Is it possible to add an additional property using a property decorator?

The current approach I am taking seems to be incorrect.

const RoProp = () => {
  return <T>(target: T, memberName: keyof T) => {
    const roPropName = `${String(memberName)}_ro`;
    let roPropVal = false;
    Object.defineProperty(target, roPropName, {
      set(v: boolean) {
        roPropVal = v;
      },
      get(): boolean {
        return roPropVal;
      },
      enumerable: true,
    });
  };
};

class ExampleClass {
  @RoProp() testProp: string;

  // Property 'testProp_ro' does not exist on type 'ExampleClass'.(2339)
  constructor({ testProp, testProp_ro }: ExampleClass) {
    this.testProp = testProp;
    this.testProp_ro = testProp_ro; // Property 'testProp_ro' does not exist on type 'ExampleClass'.(2339)
  }
}

const exampleInst = new ExampleClass({
  testProp: "a test",
  testProp_ro: false, // Argument of type '{ testProp: string; testProp_ro: boolean; }' is not assignable to parameter of type 'ExampleClass'. Object literal may only specify known properties, and 'testProp_ro' does not exist in type 'ExampleClass'.(2345)
});
exampleInst.testProp_ro = true; // Property 'testProp_ro' does not exist on type 'ExampleClass'.(2339)

Playground Link: Trying to create property with decorator

What is the correct way to achieve this task, if possible at all?

Answer №1

If I had the ability to enhance my class with a utility type?

While it is simple to add properties to types, the challenge lies in ensuring that classes declare these properties to prevent errors.

For example, when extending through implementation:

type Enrich<T extends object> = T & {
  [K in keyof T as `${Exclude<K, symbol>}_enriched`]: boolean;
}

interface ISampleClass {
  sampleProp: string;
}

class SampleClass implements Enrich<ISampleClass> {
  @EnrichedProp() sampleProp: string;

  // Still need to explicitly declare
  sampleProp_enriched: boolean;

  constructor({ sampleProp, sampleProp_enriched }: Enrich<ISampleClass>) {
    this.sampleProp = sampleProp;
    this.sampleProp_enriched = sampleProp_enriched;
  }
}

Encountering an issue arises when attempting to use a base class due to lack of initialization. To resolve this, one can make the property abstract, but then re-declaration of the original property is required:

abstract class SampleClassCore {
  // Error occurs without `abstract`
  @EnrichedProp() abstract sampleProp: string;
}

class SampleClass extends SampleClassCore
  implements Enrich<SampleClassCore> {
  // Still needs explicit declaration  
  sampleProp: string;
  sampleProp_enriched: boolean;

  constructor({ sampleProp, sampleProp_enriched }: Enrich<SampleClassCore>) {
    super();

    this.sampleProp = sampleProp;
    this.sampleProp_enriched = sampleProp_enriched;
  }
}

Exploring mix-in classes also led to confusion and multiple errors.

Perhaps there are some advanced type techniques that have escaped my notice or knowledge.

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

I'm confused why this particular method within a class is not being inherited by the next class. Rather than seeing the expected extension, I am presented with - [Function (

Working fine with the Person class, the register() function displays the correct return statement when logged in the console. However, upon extending it to the Employee class, instead of the expected return statement, the console logs show [Function (anon ...

What is the process for integrating the node-menu package into my project without utilizing the require statement?

Is there a way to incorporate node-menu into my TypeScript project without using require, like this: const menu = require('node-menu'); Whenever I attempt to import node-menu into my project, I encounter the following errors: https://i.sstatic. ...

Discover the simple steps to include row numbers or serial numbers in an angular2 datagrid

Currently, I am utilizing angular2 -datatable. Unfortunately, I am facing an issue where the correct row numbers are not being displayed in their corresponding rows. Whenever a user moves to the next page using the paginator, the datatable starts countin ...

Issue with populating virtual IDs in NestJS mongoose schema containing an array of schemas

In the schema provided below, I have defined the structure for Map, Marker, and Desk: export type MapDocument = Map & Document @Schema({ timestamps: true, versionKey: false, id: true }) export class Map { constructor(partial?: Partial< ...

"TypeScript error: Arrays in interfaces are found to be undefined and not compatible

I'm struggling with correctly implementing the Typescript React Props syntax. In my project, I have an array of people, each of whom may have zero to many cars. There is a people container that holds all the people, each person has a car container tha ...

The Action-Reducer Mapping feature is encountering a type error when handling multiple types of actions

Earlier today, I posed a question about creating a mapping between redux action types and reducers to handle each type explicitly. After receiving helpful guidance on how to create the mapping, I encountered an error when attempting to use it in creating ...

Error: The argument provided cannot be assigned to a parameter that requires a string type, as it is currently a number

Currently, I am in the process of migrating some older websites to TypeScript. However, I keep encountering a type error during the build process. The specific error message is Type error: Argument of type 'number' is not assignable to parameter ...

Having trouble implementing conditional rendering for components sourced from a .json/.ts file in Angular

Presented below is the JSON file which includes a .text component with a text field and a .radio component featuring a radio button. How can we efficiently display them conditionally based on the content of the .json file? Here's the contents of the ...

Swapping Out Imports with Window Objects in TypeScript

After creating a TypeScript module that relies on a third-party library, the JavaScript output from compilation includes a statement using require: "use strict"; var dexie_1 = require("dexie"); var storage; (function (storage) { ... })(storage || (stora ...

The return type of a getter is `any` if the object contains a method and is processed by a generic function

I am facing an issue with my code where the getter's return type is set to any, even though the actual return type should be clear. There are certain additional functions triggering this behavior: // This is necessary for reproduction const wrapperFun ...

Tips for adjusting the angle in SVG shapes

I have designed an svg shape, but I'm struggling to get the slope to start from the middle. Can someone please provide assistance? <svg xmlns="http://www.w3.org/2000/svg" fill="none"> <g filter="url(#filter0_b_1_2556)"&g ...

Enhance autocomplete functionality by incorporating a left icon feature for text fields within the autocomplete component

I have a component with autocomplete functionality that displays tags Autocomplete with tags and I am trying to add a left icon, but only the right icon is functioning correctly. Current Issue When I add a left icon, it shows up but prevents the renderi ...

Having trouble getting my specialized pipe (filter) to function properly in Angular 2

I have implemented a custom pipe for filtering data in my table. Oddly, when I enter a search string into the input box, it correctly prints 'found' in the console but no rows are displayed in the table. However, if I remove the pipe altogether, ...

Fastest method to invoke a potentially undefined function

With a background in C#, I am familiar with the null-conditional operator which allows you to call a function while avoiding a potential null-reference exception like this: Func<int> someFunc = null; int? someInteger = someFunc?.Invoke(); // someInte ...

How is it possible to access a variable in a function that hasn't been declared until later?

While working on a Dialog component, I had an unexpected realization. export const alert = (content: string) => { const buttons = [<button onClick={()=>closeModal()}>ok</button>] // seems alright // const buttons = [<button onCli ...

It is impossible to declare a class attribute that has the same type as the class itself, creating a self-referencing hierarchy

My Angular model class has properties that reference the class itself: export class ItemCategory { constructor( public parentCategory?: ItemCategory, public subCategories?: ItemCategory[], public name?: string, public description?: st ...

Issue with updating Angular list reference when deleting an item

My current task involves implementing a feature that displays selected items from a hierarchical structure on the right side. slice.component.ts : import { Component, Input, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core&a ...

Stuck at loading: Electron encountering issues with Aurelia Navigation Setup

UPDATE 1: An unexpected challenge has arisen As I endeavored to install and configure Aurelia Navigation with Typescript and Electron by following these instructions: https://github.com/aurelia/skeleton-navigation/tree/master/skeleton-typescript I succe ...

The parameters provided in TypeScript do not align with any signature of the call target

In JavaScript, a function can be called with any number of parameters. If a parameter is not passed, it will default to undefined without causing an error. Below is a code snippet for reference: function test(a,b){ if(b){console.log(b)} else{console ...

Transmitting form data inputted by the user to a modal that resides in the same component, all without the need for child or parent components or

In need of a solution where users can input answers to questions and have all the entered data displayed in a popup alongside the respective question. If a user chooses not to answer a question, I do not want that question or any related information to be ...