Stack Exchange
Create extra boolean property with name suffix `_ro`
Can I create an extra property with a property decorator?
The way I'm trying to do this obviously does not work.
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
How can this be done, if at all?

To my knowledge you cannot modify the compile time type using decorators.

I was afraid this would be the case… ¯_(ツ)_/¯ . @H.B. Thanks for answering anyway.
Could there be another solution to reach the goal, perhaps?
I think of some kind of type manipulation , maybe? If I could extend my class with a utility type?

I added an answer showing some attempts at this. What is it you are trying to achieve, though? Maybe a completely different approach (e.g. not using classes at all) could address that better.


If I could extend my class with a utility type?

It is easy to add properties to types, the main issues is that classes still need to declare the properties to not cause errors.
E.g. when extending it via implements:
type Roify<T extends object> = T & {
  [K in keyof T as `${Exclude<K, symbol>}_ro`]: boolean;
}

interface IExampleClass {
  testProp: string;
}

class ExampleClass implements Roify<IExampleClass> {
  @RoProp() testProp: string;

  // Still needs to be declared
  testProp_ro: boolean;

  constructor({ testProp, testProp_ro }: Roify<IExampleClass>) {
    this.testProp = testProp;
    this.testProp_ro = testProp_ro;
  }
}

When trying to use a base class the issue comes from lack of initialization, so to fix that you could make the property abstract, but then you have to declare even the origin property again:
abstract class ExampleClassCore {
  // Error without `abstract`
  @RoProp() abstract testProp: string;
}

class ExampleClass extends ExampleClassCore
  implements Roify<ExampleClassCore> {
  // Still needs to be declared  
  testProp: string;
  testProp_ro: boolean;

  constructor({ testProp, testProp_ro }: Roify<ExampleClassCore>) {
    super();

    this.testProp = testProp;
    this.testProp_ro = testProp_ro;
  }
}

I also tried to use mix-in classes but that resulted in even more of a mess and numerous errors.
Maybe there is some type wizardry that I have overlooked or do not know about, though.

I was also playing a bit further, but reached no real feasable result as well. If the properties need to be defined, the whole point of the effort is redundant. With typescript this seems not to be possible ATM. Many thanks for trying.

That what brought me to such ideas was at least partially by the habits in Rails and Ruby, where meta programming is quite easy, simple and powerful. Of course without the hurdles of strict typeing like with typescript.
