2

I need my getters & setters to be enumerable, however, TypeScript emits ES6 class syntax, and class properties are not enumerable in javascript.

Is there a typescript config or something else I can do that forces class properties to be emitted as Object.defineProperty() with enumerable: true?

Edit: It looks like this can be achieved with property decorators, how might that be done in this case?

Example:

class User {
 public roles: string[];
 
 get isAdmin() {
    return this.roles.some((role) => role === 'admin');
 }
}

// This should be `true` not `false`
console.log(Object.keys(new User()).includes("isAdmin"))

If a User instance gets passed into a Vuex store for instance, the non-enumerable members are dropped, and isAdmin no longer exists.

Douglas Gaskell
  • 9,017
  • 9
  • 71
  • 128
  • Why dont you just use an enum for the type and make the values of the enum strings? – Lukas Germerott Dec 08 '21 at 20:36
  • 1
    Duplicate of https://stackoverflow.com/questions/34517538/setting-an-es6-class-getter-to-enumerable, maybe. You may be better off with a `serialize` method that spits out a plain JS object. – Alex Wayne Dec 08 '21 at 20:38
  • 1
    Enumerability is not the only thing that affects property keys. Do you want `isAdmin` to be an *own* property as well as enumerable? Can you make your code here a [mre] which shows some unexpected/undesirable behavior (like, you want `console.log(Object.keys(new User()).includes("isAdmin"))` to output `true` and not `false`. – jcalz Dec 08 '21 at 20:39
  • 1
    @LukasGermerott Because this is an example not necessarily the actual code that needs this behavior? Sure the roles could be enums, but that's not the point. – Douglas Gaskell Dec 08 '21 at 20:50
  • @AlexWayne The difference here is that TS *emits* JS, I shouldn't need to make calls to `Object.defineProperty()`, that can be emitted. The question is, how? – Douglas Gaskell Dec 08 '21 at 20:53
  • @jcalz Added an output example. I imagine the method for coercing TS into emitting the JS that uses `Object.defineProperty()` would also be extendable for me to change other options. However, it would be an *own* property by default anyways (At least based on what I just tested). – Douglas Gaskell Dec 08 '21 at 20:58
  • 1
    @DouglasGaskell No, there is no way to do that. `class User { get isAdmin() {…} }` is valid JS, is what is emitted by the TypeScript compiler, and does the same thing: define a non-enumerable getter on the prototype. You cannot change that with a compiler setting, you must define the property in a different way. – Bergi Dec 08 '21 at 21:01
  • @Bergi, well you can target ES5 which will downlevel classes entirely, but there's no compiler option to decide to emit a getter as an instance property... and why would there be? The point of downleveling is to make something that acts like the same code would with a newer ES target. So yeah, you have to do [something else](https://tsplay.dev/wOzQRW) if you want this behavior. – jcalz Dec 08 '21 at 21:10
  • 1
    So... the answer to the question as asked is either just "no, there is no compiler setting to do this for you", or this question is a duplicate of https://stackoverflow.com/questions/34517538/setting-an-es6-class-getter-to-enumerable. Either way there's not much to be done here I think. ‍♂️ – jcalz Dec 08 '21 at 21:11
  • @jcalz TS is essentially compiled, and that compilation can be controlled to some degree to insert behavior. After some more searching it looks like this can be achieved with decorators. I'm not quite sure how this would be a dupe of a JS question asking how to do that *in JS* when the point here is to achieve this transparently with TS, which *seems* possible with decorators. – Douglas Gaskell Dec 08 '21 at 22:17
  • I've edited the tags to match – Douglas Gaskell Dec 08 '21 at 22:19
  • 1
    Decorators are also a [proposed JavaScript feature](https://github.com/tc39/proposal-decorators) although they were added to TypeScript when the proposal was only in Stage 2 which is probably too early (nowadays they wait until a proposed JS feature is Stage 3 before adding it to TS). So if you do it with decorators you would still be doing it [in (experimental) JS](https://www.sitepoint.com/javascript-decorators-what-they-are/) and not taking advantage of some TS-specific compiler behavior... so it could still be an answer to the other question. – jcalz Dec 09 '21 at 00:11
  • 1
    I just added [an answer over there](https://stackoverflow.com/a/70283576/2887218) which shows a way to do this with decorators, in case you're interested. – jcalz Dec 09 '21 at 01:09
  • Does this answer your question? [Setting an ES6 class getter to enumerable](https://stackoverflow.com/questions/34517538/setting-an-es6-class-getter-to-enumerable) – Alex Wayne Dec 09 '21 at 05:07
  • I don't think this can be done with property decorators since those don't get access to the class instance or the constructor; they just get the prototype. You need to wrap the constructor to mess with instances, so this would have to be a class decorator. Please let me know if [that answer](https://stackoverflow.com/a/70283576/2887218) would work for you here. – jcalz Dec 09 '21 at 15:10

0 Answers0