Mobx-state-tree: Incorrect behavior of types checking when using generic creation function

Created on 2 Oct 2018  路  11Comments  路  Source: mobxjs/mobx-state-tree

const SomeModel = types.model({
  id: types.number
})

const createSomeStore = <S, T>(modelType: IType<S, T>) => {
  const someStore = types.model({
      array: types.optional(types.array(modelType), [])
  })
  .views(self => ({
      get arrayGetter() {
          return self.array
      }
  }))

  return someStore
}

const store = createSomeStore(SomeModel).create()
const a = store.array[0]!.id // ERROR, Property 'id' does not exist on type 'NonNullable<T>'
const b = store.arrayGetter[0]!.id // OK, id is number
Typescript question

Most helpful comment

Actually I assume you are not using MST3, but v2, since IType has three types from v3 (C, S and T)

Anyway, this is the proper answer for MST3.5

const createSomeStore = <P extends ModelProperties, O, C, S, T>(modelType: IModelType<P, O, C, S, T>) => {
    const someStore = types
        .model({
            array: types.array(modelType)
        })
        .views(self => ({
            get arrayGetter() {
                return self.array
            }
        }))

    return someStore
}

const store = createSomeStore(SomeModel).create()
const a = store.array[0]!.id // ok, id is number
const b = store.arrayGetter[0]!.id // OK, id is number

All 11 comments

which typescript version and MST version?

ah I think what's wrong is the createSomeStore function definition, try
const createSomeStore = <T extends IAnyType>(modelType: T) => {

or actually IAnyModelType if it has to be a model

Actually I assume you are not using MST3, but v2, since IType has three types from v3 (C, S and T)

Anyway, this is the proper answer for MST3.5

const createSomeStore = <P extends ModelProperties, O, C, S, T>(modelType: IModelType<P, O, C, S, T>) => {
    const someStore = types
        .model({
            array: types.array(modelType)
        })
        .views(self => ({
            get arrayGetter() {
                return self.array
            }
        }))

    return someStore
}

const store = createSomeStore(SomeModel).create()
const a = store.array[0]!.id // ok, id is number
const b = store.arrayGetter[0]!.id // OK, id is number

Closing since it is about an old version and there's a way to solve it on the new version. Feel free to reopen it if it doesn't solve your issue :)

@xaviergonz: We're using the factory approach as above, which was working prior to upgrading to 3.6. https://github.com/mobxjs/mobx-state-tree/pull/1043. Now it produces the following typescript error with TS 3.1.3.

Generic type 'IModelType<PROPS, OTHERS, CustomC, CustomS>' requires between 2 and 4 type arguments.

Changing it to modelType: IModelType<P, O, C, S> or even modelType: IModelType<P, O> will freeze Visual Studio Code and npm run dev compilation freezes as well.

Looks like I'm missing something.

T it not there anymore for IModelType, I just tried this (Typescript 3.1.3) and it worked

const createSomeStore = <P extends ModelProperties, O, C, S>(modelType: IModelType<P, O, C, S>) => {
    const someStore = types
        .model({
            array: types.array(modelType)
        })
        .views(self => ({
            get arrayGetter() {
                return self.array
            }
        }))

    return someStore
}

const store = createSomeStore(SomeModel).create()
const a = store.array[0]!.id // ok, id is number
const b = store.arrayGetter[0]!.id // OK, id is number

then again I'd try this to make it future proof, it is a better solution

const createSomeStore = <IT extends IAnyModelType>(modelType: IT) => {
    const someStore = types
        .model({
            array: types.array(modelType)
        })
        .views(self => ({
            get arrayGetter() {
                return self.array
            }
        }))

    return someStore
}

const store = createSomeStore(SomeModel).create()
const a = store.array[0]!.id // ok, id is number
const b = store.arrayGetter[0]!.id // OK, id is number

Thanks @xaviergonz. I can confirm that both solutions are working as expected. Now I just need to figure out why type checking is no longer working in our factories.

No problem, btw, if you are seeing freezes with vs-code / autocompilation it might be due to lots of compilation errors. Usually running tsc stand alone and giving the node process more ram works (until those errors are fixed)
e.g
NODE_OPTIONS=--max_old_space_size=1900 tsc
where 1900 is the limit in MB of the ram to be used (I think it defaults to 256mb)

@xaviergonz thanks for the suggestion how to tackle vs-code freezes.

Just if anybody runs into a similar issue. The issue with our factories boiled down to the fact that in one actions block we had defined the return value as any in order to modify it based on some condition.
This turned out to throw of TypeScript completely. Slightly changing the code made TypeScript work again as expected.

.actions((self : any) => {
      function a(){}
      function b(){}

      // Bad
      // const api: any = {
      //   a
      // }

      // if (true){
      //   api.b = b 
      // }
      // Good
      const api = {
        a
      }

      if (true){
        (api as any).b = b 
      }

      return api
    })

That's weird, good to know though

Was this page helpful?
0 / 5 - 0 ratings