mobx: `[MobX] Cannot decorate undefined property` when using `makeObservable` with `babel-preset-typescript`

Hi,

I am trying to upgrade to Mobx@6. This is my before and after class:

// Before
class Person {
  @observable age!: number;
}

// After
class Person {
  age!: number; // EDITED: forgot to add this in the original example

  constructor() {
    makeObservable(this, {
      age: observable
    });
  }
}

When I run the app, the age is not yet initialized, and the error [MobX] Cannot decorate undefined property: 'age' is thrown.

I understand what it means here: based on the doc for makeObservable, only existing property will be made observable. Also I understand that this could be bad typescript usage since I use !: but this is somewhat necessary for our use-case (though I believe we could think of some workaround for this), but more importantly, this used to work fine in mobx@5 so I wonder if this is an expected breaking change in mobx@6?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 16 (8 by maintainers)

Most upvoted comments

Just an update, this problem has been resolved in babel@7.12.0. Everything should work as expected if we follow the migration guide and turning on the flow allowDeclareFields for @babel/preset-typescript

@mweststrate I think I found the root cause. What happened is very nuanced.

  age!: number;

will be treated conceptually the same as declare age: number by @babel/plugin-transform-typescript. So the behavior is to strip this field from class. So when I print out the object before calling makeObservable, I don’t even see the field.

But if I do:

  age: number;    // typescript will complain because I don't initialize this, but this will still compile fine

When I print out the object before calling makeObservable, I see the field with value undefined within the object.

Now on niche thing they did was avoiding stripping the field if it was decorated so in that sense, if I do:

  @observable age!: number;

This should work fine, though I would love to get rid of decorators. Now my question is what should I do here:

  1. Should I expect Mobx to adjust to fix this use-case? - I’m not sure what you can do since the field does not even show up in the object
  2. Should I file an issue with babel? - not sure neither since I don’t have much deep knowledge on this topic and their discussion seems to be going in the direction of stripping the field with definite assignment operator
  3. Should I opt to use @observable as a workaround until I convert fully to use some kind of factory pattern and only call makeObservable when I know the field has been initialized (I can do this, though it requires some amount of refactoring to be done in my project though).

Sorry if the questions seem a bit scattered, I would come back tomorrow to prune them, but I thought I should give you an update to save time.

@mweststrate Sorry I did not search properly 😦 I read through point 5 and went on to search up that flag in Typescript doc and thought that was a niche TS feature that we didn’t have to care about at all 😃. Now it all makes sense. Thank you so so much!

Yes: https://mobx.js.org/migrating-from-4-or-5.html#getting-started bullet 4 / 5. For background, see https://github.com/mobxjs/mobx/issues?q=Cannot+decorate+undefined+property first couple of hits. So you will need to declare your field explicitly in any case (without TS will complain), and if you don’t change your build config, you will have to initialise it as well.