7

Here is a sampling of SO questions for Typescript getters/setters: from 2015, Jan 2018, Sept 2018, among others.

That said, what is the recommended way for defining Typescript types for getters/setters in a vanilla JS object? That is, I would like a plain old javascript object without the class syntax, and to understand the best practices for typing. Here's the interface -

interface Store {
   activeEvent: Date
}

A few constraints for this object -

No new to instantiate

No duplicates - this doesn't work

const Store: Store = {
  activeEvent: new Date(),
  get activeEvent() {
    return this.activeEvent;
  },
}

No dangling underscores. Airbnb's eslint config is yelling at this -

const Store: Store = {
  _activeEvent: {},
  get activeEvent() {
    return this._activeEvent;
  },

Ideally I would like to have default values (that is, if I drop the key, the error goes away, but on first access activeEvent is undefined). I suppose I could init and return this.activeDate if it is undefined, which works, but get is actually get & set in that scenario -

 get activeDate() {
    if(this.activeDate){ return this.activeDate }
    const date = new Date()
    this.activeDate = date;
    return date;
  },

I would rather not generate duplicate keys with getters and setters just to have getters and setters.

Interestingly enough, the fact that the getter/setter have the same key doesn't error out, it's just when they match the private-ish variable, which makes me think I am doing something wrong. I appreciate any insight.

Error on getter setter

nrako
  • 2,952
  • 17
  • 30
  • 3
    `get activeEvent() { return this.activeEvent; },` <- this code is an infinite loop – zerkms Jan 28 '20 at 23:27
  • 4
    "*No dangling underscores*" silly restriction, if you ask me. I'd personally just disable that rule. If you have `get activeEvent()` then you can't do `return activeEvent;` from inside it, since that would trigger the same getter. So you need the property and the getter named differently. Using an underscore is a well known convention for "private". You could just name it `this.xxxActiveEventxxx` or something - just append other stuff than an underscore, but it's silly. And if you name it as a *synonym* rather than the exact same thing, it's harder to maintain. – VLAZ Jan 28 '20 at 23:31
  • 1
    Why do you even want getters? Don't write them just to have getters. It's not the right way to have a default value. If you want a default value just add that default value instead of the getter – Lux Jan 28 '20 at 23:43
  • @zerkms heh, that is an infinite loop. I suppose the TS error is helpful. – nrako Jan 29 '20 at 06:35
  • @VLAZ, I agree the restriction lends itself to a maintenance problem. – nrako Jan 29 '20 at 06:36
  • @Lux, the example is obviously contrived. I am looking at some options for dynamically calculated properties, and would like a strongly-typed object with initial state living directly w/ the definition. I haven't used a getter/setter w/ typescript before, which led me here... – nrako Jan 29 '20 at 06:46

1 Answers1

9

Wrap your object creation in a closure, and keep variables that aren't part of the interface outside the object:

function createStore() {
  let activeEvent = new Date();
  return {
    get activeEvent() {
      return activeEvent;
    },
  };
}

This avoids the name collision/infinite loop issue you're currently running into - the object you return has a .activeEvent getter, but the variable backing it lives outside the object, in the scope of the createStore function.

suddjian
  • 1,986
  • 2
  • 16
  • 25
  • 1
    typo? `let activeDate` should be `let activeEvent` – jcalz Jan 29 '20 at 01:00
  • From a data standpoint the closure is fine. I was hoping for some Typescript conventions, but you do answer the question as originally asked. Thanks for the note. – nrako Jan 29 '20 at 07:04
  • I don't think there are any special typescript conventions that will help here. If you want the data to be part of the object, I advise disabling the eslint rule as mentioned by other commenters. – suddjian Jan 30 '20 at 07:16