mobx-state-tree icon indicating copy to clipboard operation
mobx-state-tree copied to clipboard

Circular Dependency Issue - TS typings

Open oviava opened this issue 5 years ago • 8 comments

Question

  • [x] I've checked documentation and searched for existing issues
  • [x] I tried the spectrum channel

Following a lot of hours of research, I ran into this: https://github.com/mobxjs/mst-gql/pull/140. Tested it in my codebase and it's finally a 'sort' of fix for the typings on circular dependencies.

Are you guys considering including it in MST ?!

oviava avatar Nov 27 '19 16:11 oviava

Posted this from my phone, I'll update it further when I get home.

oviava avatar Nov 27 '19 16:11 oviava

So coming back to this, for dealing with circular references, an idea - copying most of it from @godness84


const MyModel = types.model(
  refExample1: types.reference(types.late((): any => RefExample1)),
  refExample2: types.reference(types.late((): any => RefExample2)),
  refExample3: types.array(types.reference(types.late((): any => RefExample3))),
);


type Without<T, K> = Pick<T, Exclude<keyof T, K>>;

// this needs better typing
type ExtendRefs<K> = {
  [P in keyof K]: null extends K[P]
    ? never
    : K[P] extends IArrayType<infer O>
    ? IMSTArray<Instance<IReferenceType<O>>>
    : K[P] extends IAnyComplexType
    ? Instance<IReferenceType<K[P]>>
    : never;
};

type WithRefsModelType<T extends IAnyModelType, OTHERS> = Instance<
  T extends IModelType<infer P, infer O, infer C, infer S>
    ? IModelType<Without<P, keyof OTHERS>, O & ExtendRefs<OTHERS>, C, S>
    : never
>;

interface IRefs {
  refExample1: typeof RefExample1;
  refExample2: typeof RefExample2;

  refExample3: IArrayType<typeof RefExample3>;
}

export type IMyModelType = WithRefsModelType<typeof MyModel, IRefs>;

Maybe someone with more TS knowledge can build on this and provide it as an API in MST ?

oviava avatar Nov 28 '19 12:11 oviava

Tagging in @mweststrate cause I think he followed the mst-gql issue as well

oviava avatar Nov 28 '19 12:11 oviava

Sorry, could you summarize it without referring to an entire thread? Note that in MST there is no code generation unlike in mst-gql, so the options are a bit more limited, as we need to have TS infer the types, rather than generating them.

mweststrate avatar Nov 28 '19 13:11 mweststrate

Sorry, basically they added a solution to their generator to better provide types on circular dependencies without typescript blowing up

const MyModel = types.model(
  // this blows up in TypeScript if RefExample1 references MyModel - circular dependencies
  refExample1: types.reference(types.late((): Instance<typeof RefExample1> => RefExample1)),
  refExample2: types.reference(types.late((): any => RefExample2)),
  refExample3: types.array(types.reference(types.late((): any => RefExample3))),
);

With the above I'm thinking if somehow we can integrate something in MST's API like


import { Instance, IModelType, IMSTArray, IReferenceType, IAnyComplexType, IArrayType } from 'mobx-state-tree';

type Without<T, K> = Pick<T, Exclude<keyof T, K>>;

// this needs better typing and only covers array/instance reference
type ExtendRefs<K> = {
  [P in keyof K]: null extends K[P]
    ? never
    : K[P] extends IArrayType<infer O>
    ? IMSTArray<Instance<IReferenceType<O>>>
    : K[P] extends IAnyComplexType
    ? Instance<IReferenceType<K[P]>>
    : never;
};

type WithRefsModelType<T extends IAnyModelType, OTHERS> = T extends IModelType<
  infer P,
  infer O,
  infer C,
  infer S
>
  ? IModelType<Without<P, keyof OTHERS>, ExtendRefs<OTHERS> & O, C, S>
  : never;

To provide better typings for these circular references

In the example above we can do

interface IRefs {
  refExample1: typeof RefExample1;
  refExample2: typeof RefExample2;

  refExample3: IArrayType<typeof RefExample3>;
}

export interface IMyModelType extends Instance<WithRefsModelType<typeof MyModel, IRefs>> {};

And then in actions for example or where the instance is used

MyModel.actions(self => {
   const _self = self as IMyModelType;
   // solution now is (_self.refExample1  as IExample1Instance).stuff
   _self.refExample1.stuff // this now has typings on circular deps without having to cast it
})

oviava avatar Nov 28 '19 13:11 oviava

I hope @xaviergonz not gonna mind linking him but I've seen he worked a lot on the TS/typings for MST :)

oviava avatar Nov 28 '19 14:11 oviava

@oviava This seems really cool, do you have any update on that? Have you used it yourself in a project? Any pros/cons?

PEZO19 avatar Apr 16 '23 21:04 PEZO19

Hey @oviava - it's been a while since we heard from you on this MST thread, but I know we're actively looking to improve our TS here. Are you interested in exploring this any more?

I'm going to put the TS label on this issue for now, and happy to talk about if/when/how to move it forward.

coolsoftwaretyler avatar Jun 30 '23 03:06 coolsoftwaretyler