Hi,can this be done? Typescript is screaming at me right now:
export const Folder = types.model({
name: types.frozen as IType<string ,string>,
folders: types.array(Folder) // here I'm using block scoped variable before I define it
});
Sure, you should use types.array(types.late(() => Folder))! Is there a reason why you use types.frozen and then cast to string?
@mattiamanzati Thanks for getting back to me. Defining it like this leads to typescript error:
[ts] 'Folder' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initialiser. Modelling
Here is my definition
export const Folder = types.model({
name: types.string,
folders: types.array(types.late(() => Folder))
});
Unfortunatelly event this does not work:
export const Folder: FolderType = types.model({
name: types.frozen as IType<string ,string>,
folders: types.array(types.late(() => Folder))
});
export type FolderType = typeof Folder.Type;
As this leads to 'circular reference error ;(
Wit that "frozen" I tried to model that that field is not reactive, but I guess it's useless.
I spent quite some time on this and I just cannot find a way how can I make this type safe in Typescript.
Unfortunately TS is'nt able to handle circular reference correctly right now, you need to provide the generic type for the late parameter, check out the tests folder for late! :)
https://github.com/mobxjs/mobx-state-tree/blob/master/test/late.ts#L23
@mattiamanzati that does not work as I get error: "[ts] 'Folder' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer."
This is how I have it:
export interface FolderType {
name: string,
folder: FolderType
}
export const Folder = types.model({
name: types.string,
folders: types.array(types.late<any, FolderType>(() => Folder))
});

Ok, I got a solution:
export type FolderType = IModelType<any, {
name: string,
folders: IObservableArray<FolderType>
}>
export const Folder: FolderType = types.model({
name: types.string,
folders: types.array(types.late<any, FolderType>(() => Folder))
});
I was almost there several times but I kept definining folder type like this:
export type FolderType = IModelType<{
name: string,
folders: IObservableArray<FolderType>
}, {}>
which led to problems as I do not understand what S and T represent in IType definition.
If you would be so kind, can you tell me what do S and T types represent? You can close the issue as well. Thanks.
[EDIT] Oh, this is NOT a solution unfortunately as those params 'name' and 'folders' do NOT appear in type definition
Unfortunately we can't do much, i'ts a known issue with TS circular references. I will update if a solution appears.
@mattiamanzati I'll work on it, but can you please let me know what S and T represent in type definitions? Thanks!
In IType S is the type of the snapshot, T is the type of the instance. e.g. for arrays of number S is number[] and T is IObservableArray
Ok, thanks .. and here is a working example. I made it a bit more complex so that I portray more things:
export const Story = types.model(
{
name: '',
info: ''
}
);
export type StoryType = typeof Story.Type;
export type FolderType = {
name: string,
folders: IObservableArray<FolderType>, // recursive
stories: IObservableArray<StoryType> // different model type
};
export type FolderModel = IModelType<FolderType, FolderType>
export const Folder: FolderModel = types.model({
name: types.string,
folders: types.array(types.late<any, FolderType>(() => Folder)),
stories: types.array(Story)
});
const t = Folder.create();
Uff .. time for a beer. But glad we were able to solve this.
And here is how your example can be defined with no Typescript errors and fully type safe:
type NodeInstance = {
childs: NodeInstance[]
}
type NodeModel = IModelType<NodeInstance, NodeInstance>
// TypeScript is'nt smart enough to infer self referencing types -> We are smarter ;)
const Node: NodeModel = types.model({
childs: types.optional(types.array(types.late<any, NodeInstance>(() => Node)), [])
});
@tomitrescak Thanks for this, the beer is on me. Looks like LUIS will get smarter :).
@RainerAtSpirit ha! you got that right. LUIS runs bunch of tests and depend a lot on asynchronous execution. I was getting inconsistent results up until I reprogrammed the whole thing to mobx-state-tree. It's still not 100% done, but soon will be released along with tutorial for fuse-test-runner.
Most helpful comment
And here is how your example can be defined with no Typescript errors and fully type safe: