Skip to content
This repository has been archived by the owner on Sep 29, 2020. It is now read-only.

Issues with readonly and enumreable #148

Closed
JonWallsten opened this issue Feb 17, 2018 · 4 comments
Closed

Issues with readonly and enumreable #148

JonWallsten opened this issue Feb 17, 2018 · 4 comments

Comments

@JonWallsten
Copy link

I'm currently testing these decorators in a typescript boilerplate project, but I've run into some issues. I might totally miss understand how they work, in that case please enlighten me.
When using read-only and nonenumerable on my props I can't get any values at all to stick.
I also get nog warnings about the prop being read only when trying to set it.

Consider the following code:

export class Test {
  prop: string = 'Initial'

  @readonly
  propReadOnly: string = 'Initial'

  @nonenumerable
  propNonEnumerable: string = 'Initial'

  constructor() {
    console.log('Constructor: ', this);
    console.log('Log nonenumerable prop from within constructor: ', this.propNonEnumerable);
    console.log('Log read-only prop from within constructor: ', this.propReadOnly);
  }

  getProp(): string {
      return this.prop;
  }
  getNonEnumerableProp(): string {
      return this.propNonEnumerable;
  }
  getReadOnlyProp(): string {
      return this.propReadOnly;
  }

  setProp(value: string): void {
    this.prop = value;
  }
  setNonEnumerableProp(value: string): void {
    this.propNonEnumerable = value;
  }
  setReadOnlyProp(value: string): void {
    this.propReadOnly = value;
  }
}

Test

import { Test } from './test'

const test = new Test();

console.group('Class members:');
console.log(Object.keys(test));

console.groupEnd();

console.group('Prop');
console.log('test.prop (initial): ', test.prop);
console.log('test.getProp() (initial):', test.getProp());

test.prop = 'New value';
console.log('test.prop (assign): ', test.prop);
console.log('test.getProp() (assign): ', test.getProp());

test.setProp('Final value');
console.log('test.prop (set):', test.prop);
console.log('test.getProp() (set):', test.getProp());
console.groupEnd();

console.group('Prop Read-only');
console.log('test.propReadOnly (initial):', test.propReadOnly);
console.log('test.getReadOnlyProp() (initial):', test.getReadOnlyProp());

test.propReadOnly = 'New value';
console.log('test.propReadOnly (assign):', test.propReadOnly);
console.log('test.getReadOnlyProp() (assign):', test.getReadOnlyProp());

test.setReadOnlyProp('Final value');
console.log('test.propReadOnly (set):', test.propReadOnly);
console.log('test.getReadOnlyProp() (set):', test.getReadOnlyProp());
console.groupEnd();

console.group('Prop NonEnumerable');
console.log('test.propNonEnumerable: (initial)', test.propNonEnumerable);
console.log('test.getNonEnumerableProp(): (initial)', test.getNonEnumerableProp());

test.propNonEnumerable = 'New value';
console.log('test.propNonEnumerable (assign):', test.propNonEnumerable);
console.log('test.getNonEnumerableProp() (assign):', test.getNonEnumerableProp());

test.setNonEnumerableProp('Final value');
console.log('test.propNonEnumerable (set):', test.propNonEnumerable);
console.log('test.getNonEnumerableProp() (set):', test.getNonEnumerableProp());
console.groupEnd();

Output

Constructor:  
Test {prop: "Initial"} (when inspecting: prop: "Final value")
 Log nonenumerable prop from within constructor:  undefined
 Log read-only prop from within constructor:  undefined

Class members: Prop
 ["prop"]
 0: "prop"
 length: 1
 __proto__: Array(0)

Prop
 test.prop (initial):  Initial
 test.getProp() (initial): Initial
 test.prop (assign):  New value
 test.getProp() (assign):  New value
 test.prop (set): Final value
 test.getProp() (set): Final value
 
Prop Read-only
 test.propReadOnly (initial): undefined
 test.getReadOnlyProp() (initial): undefined
 test.propReadOnly (assign): undefined
 test.getReadOnlyProp() (assign): undefined
 test.propReadOnly (set): undefined
 test.getReadOnlyProp() (set): undefined

Prop NonEnumerable
 test.propNonEnumerable: (initial) undefined
 test.getNonEnumerableProp(): (initial) undefined
 test.propNonEnumerable (assign): undefined
 test.getNonEnumerableProp() (assign): undefined
 test.propNonEnumerable (set): undefined
 test.getNonEnumerableProp() (set): undefined
@jayphelps
Copy link
Owner

jayphelps commented Mar 7, 2018

There are some known issues and non-standard ways that TS compiles decorators unfortunately.

core-decorators does not officially support TypeScript. There are known incompatibilities with the way it transpiles the output. PRs certainly welcome to fix that! - https://github.com/jayphelps/core-decorators#get-it

I haven't wanted to spend a bunch of effort on working around them because the decorator proposal in stage-2 changed the spec in very major ways that are incompatible and neither Babel nor TypeScript have implemented it yet.


That said, I'm not sure if that's related to your specific issues or not.

I also get nog warnings about the prop being read only when trying to set it.

If you're using the @readonly decorator, that should be expected. It doesn't make it readonly only for external accesses, it makes it readonly for everyone.

Perhaps you can elaborate? Sorry you're having issues!

@JonWallsten
Copy link
Author

Sorry, we can close this. I got my answer from the official Typescript repo.
microsoft/TypeScript#22048

@jayphelps
Copy link
Owner

@JonWallsten oh interesting. Thanks for following up!

@eternalphane
Copy link

@JonWallsten just fyi, I've found a way to decorate the instance property:

/**
 * @enumerable decorator that sets the enumerable property of a class field to false.
 * @param value true|false
 */
const nonenumerable = (target: any, propertyKey: string) => {
    let descriptor = Object.getOwnPropertyDescriptor(target, propertyKey) || {};
    if (descriptor.enumerable !== false) {
        Object.defineProperty(target, propertyKey, {
            enumerable: false,
            set(value: any) {
                Object.defineProperty(this, propertyKey, {
                    enumerable: false,
                    writable: true,
                    value
                });
            }
        })
    }
}

class Employee {
    @nonenumerable
    public id: number;
    public name: string;

    constructor() { 
        this.id = 1;
        console.log("this.id | Expected: 1 | Actual: ", this.id);
    }
}

var user = new Employee();
user.id = 2;
user.name = 'John Doe';
console.log("user.id | Expected: 2 | Actual: ", user.id);
console.log("Object.keys(user).length | Expected: 1 | Actual: ", Object.keys(user).length);

console.log("Object.keys(user): ", Object.keys(user));

(playground)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants