Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recursive MST models and TypeScript #417

Closed
danielduwaer opened this issue Oct 3, 2017 · 9 comments
Closed

Recursive MST models and TypeScript #417

danielduwaer opened this issue Oct 3, 2017 · 9 comments

Comments

@danielduwaer
Copy link

In the documentation of type.late (https://github.com/mobxjs/mobx-state-tree/blob/master/src/types/utility-types/late.ts), the following section of documentation specifies how we should deal with recursive MST model definitions:

// Defines a type that gets implemented later. This is useful when you have to deal with circular dependencies.
// Please notice that when defining circular dependencies TypeScript isn't smart enough to inference them.
// You need to declare an interface to explicit the return type of the late parameter function.

interface INode {
   childs: INode[]
}

// TypeScript is'nt smart enough to infer self referencing types.
const Node = types.model({
   childs: types.optional(types.array(types.late<any, INode>(() => Node)), [])
})

However, when implementing this example using TypeScript 2.5.2 (with noImplicitAny set to true), this results in the following TypeScript error:

TS7022:'Node' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

If we set noImplicitAny in our TsConfig to false, then the error is gone. However, there is no typing information available on the MST model anymore. I can instantiate Node with any snapshot, without it ever triggering a typing error. I assume the snapshot resolves to the 'any' type, thus no type checking is performed in this case.

Are there any suggestions to solve this issue?

@praxxis
Copy link
Contributor

praxxis commented Oct 3, 2017

It's not great but we've worked around this by explicitly typing the model reference:

import {types, IModelType} from 'mobx-state-tree';

interface INode {
   childs: INode[]
}

const Node: IModelType<Partial<INode>, INode> = types.model({
   childs: types.optional(types.array(types.late(() => Node)), [])
})

const node = Node.create();
node.childs; // INode.childs: INode[]

@mattiamanzati
Copy link
Contributor

Uhm, the example in the readme I think worked with 2.4 in strict mode, maybe with 2.5 with strict it now throws, so we could update to the example by @praxxis :)

@EricForgy
Copy link
Contributor

EricForgy commented May 2, 2018

Hi,

I have the same problem. Can someone give me a hand?

I look at the late test and can find this:

test("late should allow circular references", () => {
    // TypeScript is'nt smart enough to infer self referencing types.
    const Node = types.model({
        childs: types.optional(types.array(types.late(() => Node)), [])
    })
    expect(() => Node.create()).not.toThrow()
    expect(() => Node.create({ childs: [{}, { childs: [] }] })).not.toThrow()
})
test("late should describe correctly circular references", () => {
    // TypeScript is'nt smart enough to infer self referencing types.
    const Node = types.model("Node", {
        childs: types.array(types.late(() => Node))
    })
    expect(Node.describe()).toEqual("{ childs: Node[] }")
})

TypeScript is'nt smart enough to infer self referencing types.

Any idea what I should do if I'm using TypeScript? 😅

My case is a little trickier because I have other fields. For example,

const Node = types.model("Node",{
    name: types.string,
    age: types.number,
    children: types.array( types.late(() => Node )
})

How can I modify the workaround about to handle this case?

Thank you 🙌

Edit: Yay! I got it! 😃🎉

In case any other noobs (like me) stumble onto this, maybe this will help:

interface INode {
    name: string;
    age: number;
    children: INode[];
}

const Node: IModelType<Partial<INode>, INode> = types.model("Node", {
    name: types.string,
    age: types.number,
    children: types.optional(types.array(types.late(() => Node)), [])
});

@aksonov
Copy link

aksonov commented Oct 25, 2018

So am I correct that the only way to solve 'circular dependency' issues with TypeScript is to create all needed TS interfaces manually (?) and use it because MST makes everything as any (after types.late usage)? It sounds like very complex solution for big models with many fields, lists, etc...Is there any other way?

@aksonov
Copy link

aksonov commented Oct 25, 2018

@mweststrate I would appreciate your input on this

UPDT: Oh, I see your comment now: #658 (comment)

@RainerAtSpirit
Copy link
Contributor

@aksonov: While we're probably all waiting for a generic solution check out if the factory pattern in #1011 (comment) can be used as workaround for your use case.

@mweststrate
Copy link
Member

mweststrate commented Oct 26, 2018 via email

@bhagyas
Copy link

bhagyas commented Dec 5, 2018

This issue continues to exist. Adding //@ts-ignore doesn't seem to help.

@xaviergonz
Copy link
Contributor

Please check the last comments from #943

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants