添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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

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?

miguelrs, dsabanin, bhagyas, val-samonte, yang, dzcpy, liuliangsir, and clbg reacted with thumbs up emoji platon-rov and liuliangsir reacted with thumbs down emoji All reactions

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[]

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)), [])
});

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?