_Bug report_
Sandbox link or minimal reproduction code
The following minimal repro works as expected with TS 3.4 but not 3.5.
import { types, getRoot } from "mobx-state-tree";
const ChildStore = types
.model("ChildStore", {
foo: types.string,
bar: types.boolean
})
.views(self => ({
get root() {
return getRoot<typeof RootStore>(self);
}
}))
.actions(self => ({
test() {
const { childStore } = self.root;
// childStore and childStore.foo is properly inferred in TS 3.4 but not in 3.5
console.log(childStore.foo);
}
}));
const RootStore = types.model("RootStore", {
childStore: ChildStore
});
Describe the expected behavior
Typing getRoot should work with TS 3.5 as it does in 3.4.
Describe the observed behavior
As soon as RootStore is referenced by type to getRoot it breaks type inference and RootStore becomes any.
Should afaik be `return getRoot
@mweststrate But it works just fine in TS 3.4 and it's the same as in https://github.com/mobxjs/mobx-state-tree/issues/951#issuecomment-408922911.
Not sure why it changed in 3.5 (or why it _succeeded_ in 3.4), but can confirm that breaking the circular type with an interface fixes the problem:
const ChildStore = types
.model("ChildStore", {
foo: types.string,
bar: types.boolean
})
.views(self => ({
get root(): IRootStore {
return getRoot<IRootStore>(self)
}
}))
.actions(self => ({
test() {
const { childStore } = self.root
// childStore and childStore.foo is properly inferred in TS 3.4 but not in 3.5
console.log(childStore.foo)
}
}))
interface IRootStore extends Instance<typeof RootStore> {}
const RootStore = types.model("RootStore", {
childStore: ChildStore
})
Should work with the above setup in 3.14.1
I was using the views get root() approach, however with multiple ChildStore models, typescript will start to get flaky when resolving circular references. Everything would be fine, then I would reload VSCode and my RootStore would be any typed again, giving me an error.
I got it to work consistently by writing out interfaces for each of my ChildStore models as well, then creating an IRootStore interface like so:
interface IChildOne extends Instance<typeof ChildOne>{}
const ChildOne = types.model({})
.views(self => ({
get root(): IRootStore {
return getRoot(self)
},
}))
.actions(self => ({
fooAction() {
const childThreeProp = self.root.childThree.someprop
// childThreeProp is properly typed, RootStore not `any`
},
}))
// other children defined similarly here
export interface IRootStore {
childOne: IChildOne
childTwo: IChildTwo
childThree: IChildThree
}
export const RootStore = types.model({
childOne: ChildOne,
childTwo: ChildTwo,
childThree: ChildThree,
})
This method adds some boilerplate, but seems to be the most consistent solution for me until Typescript fixes the circular reference types issues. Thank you for not making this project rely on code generation :smile:
@Bkucera thank you, I managed to finally get it working with your solution! Breaking the circular type with an interface as @mweststrate suggested however did not do the trick for me (same error). Seems we are stuck with this workaround (TypeScript 3.7.4).
For anyone still having problem with typescript flakiness, I recommend classy-mst. Moving to typescript classes fixed all my typings issues.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs or questions.
Most helpful comment
I was using the views
get root()approach, however with multipleChildStoremodels, typescript will start to get flaky when resolving circular references. Everything would be fine, then I would reload VSCode and myRootStorewould beanytyped again, giving me an error.I got it to work consistently by writing out interfaces for each of my
ChildStoremodels as well, then creating anIRootStoreinterface like so:This method adds some boilerplate, but seems to be the most consistent solution for me until Typescript fixes the circular reference types issues. Thank you for not making this project rely on code generation :smile: