pmndrs / jotai

👻 Primitive and flexible state management for React
https://jotai.org
MIT License
18.72k stars 617 forks source link

Bug: atomWithMachine typescript error after updating xstate to version 4.29.0 #1000

Closed violabg closed 2 years ago

violabg commented 2 years ago

Description

I have updated xstate to version 4.29.0, and I'm getting a typescript error when adding a machine to atomWithMachine.

Expected result

no typescript error

Actual result

Argument of type '(get: Getter) => StateMachine<TimerContext, any, TimerEvents, any, BaseActionObject, ServiceMap, TypegenDisabled & { ...; } & AllowAllEvents & { ...; }>' is not assignable to parameter of type 'StateMachine<TimerContext, any, TimerEvents, any, BaseActionObject, ServiceMap, TypegenDisabled> | ((get: Getter) => StateMachine<...>)'.
  Type '(get: Getter) => StateMachine<TimerContext, any, TimerEvents, any, BaseActionObject, ServiceMap, TypegenDisabled & { ...; } & AllowAllEvents & { ...; }>' is not assignable to type '(get: Getter) => StateMachine<TimerContext, any, TimerEvents, any, BaseActionObject, ServiceMap, TypegenDisabled>'.
    Call signature return types 'StateMachine<TimerContext, any, TimerEvents, any, BaseActionObject, ServiceMap, TypegenDisabled & { ...; } & AllowAllEvents & { ...; }>' and 'StateMachine<TimerContext, any, TimerEvents, any, BaseActionObject, ServiceMap, TypegenDisabled>' are incompatible.
      The types of 'getStateNodes' are incompatible between these types.
        Type '(state: StateValue | State<TimerContext, TimerEvents, any, any, TypegenDisabled & { missingImplementations: { actions: never; delays: never; guards: never; services: never; }; } & AllowAllEvents & { ...; }>) => StateNode<...>[]' is not assignable to type '(state: StateValue | State<TimerContext, TimerEvents, any, any, TypegenDisabled>) => StateNode<TimerContext, ... 4 more ..., TypegenDisabled>[]'.
          Types of parameters 'state' and 'state' are incompatible.
            Type 'StateValue | State<TimerContext, TimerEvents, any, any, TypegenDisabled>' is not assignable to type 'StateValue | State<TimerContext, TimerEvents, any, any, TypegenDisabled & { missingImplementations: { actions: never; delays: never; guards: never; services: never; }; } & AllowAllEvents & { ...; }>'.
              Type 'State<TimerContext, TimerEvents, any, any, TypegenDisabled>' is not assignable to type 'StateValue | State<TimerContext, TimerEvents, any, any, TypegenDisabled & { missingImplementations: { actions: never; delays: never; guards: never; services: never; }; } & AllowAllEvents & { ...; }>'.
                Type 'State<TimerContext, TimerEvents, any, any, TypegenDisabled>' is not assignable to type 'State<TimerContext, TimerEvents, any, any, TypegenDisabled & { missingImplementations: { actions: never; delays: never; guards: never; services: never; }; } & AllowAllEvents & { ...; }>'.
                  Types of property 'machine' are incompatible.
                    Type 'StateMachine<TimerContext, any, TimerEvents, any, BaseActionObject, any, TypegenDisabled> | undefined' is not assignable to type 'StateMachine<TimerContext, any, TimerEvents, any, BaseActionObject, any, TypegenDisabled & { missingImplementations: { ...; }; } & AllowAllEvents & { ...; }> | undefined'.
                      Type 'StateMachine<TimerContext, any, TimerEvents, any, BaseActionObject, any, TypegenDisabled>' is not assignable to type 'StateMachine<TimerContext, any, TimerEvents, any, BaseActionObject, any, TypegenDisabled & { missingImplementations: { ...; }; } & AllowAllEvents & { ...; }>'.
                        Types of property '__TResolvedTypesMeta' are incompatible.
                          Type 'TypegenDisabled' is not assignable to type 'TypegenDisabled & { missingImplementations: { actions: never; delays: never; guards: never; services: never; }; } & AllowAllEvents & { indexedActions: IndexByType<...>; indexedEvents: Record<...> & { ...; }; invokeSrcNameMap: Record<...>; }'.ts(2345)

Reproduction

update to version 4.29.0, and add a machine to atomWithMachine

dai-shi commented 2 years ago

Thanks for reporting!

Do you know how to fix it??

davidkpiano commented 2 years ago

This is probably on our end; will take a look.

violabg commented 2 years ago

still have the same problem even after updating to xstate 4.30.1

davidkpiano commented 2 years ago

@violabg Can you make a reproducible example in CodeSandbox?

violabg commented 2 years ago

here you go

https://codesandbox.io/s/dreamy-dust-b33z7g

on line 34 you can see the typescript error

violabg commented 2 years ago

Any solution to resolve typescript miss match?

dai-shi commented 2 years ago

@Andarist Do you know if this is already an issue on jotai end?

Andarist commented 2 years ago

@dai-shi thanks for the ping. Coincidentally I've figured out a fix for this just today, some more info can be found here. I will move forward with this to fix this and similar issues.

Note that this doesn't fully fix the given repro case - the repro showcases 2 problems while the fix I'm talking about only fixes the first one. I think that I also know how to fix the second one though and gonna play around with this too.

Andarist commented 2 years ago

Actually... I might have read this wrong yesterday. I've released the fix for the mentioned issue and I fix it solves the problem in full here, the updated codesandbox is here: https://codesandbox.io/s/billowing-lake-wf3ixp?file=/src/App.tsx

violabg commented 2 years ago

sorry to bother you again, but I'm still getting typescript errors, I have updated the CodeSandbox to reflect my real use case, you can find it here, maybe it's me doing something wrong, but I can't figure it out.

https://codesandbox.io/s/awesome-drake-26p92f?file=/src/App.tsx

davidkpiano commented 2 years ago

@Andarist It seems that the typegen generic is incompatible with places where some of the generics are already specified: https://github.com/pmndrs/jotai/blob/main/src/xstate/atomWithMachine.ts#L14-L28

Andarist commented 2 years ago

Yes, when the machine already contains the typegen stuff then consuming function is supposed to handle/forward it OR specify that it wants to ignore with any in that slot (or a similar method).

I didn't figure out a way for this to work around as { enabled: true } and { enabled: false } types are fundamentally incompatible. When I think about it know, maybe we could try to make TypegenDisabled to be roughly equivalent to { enabled: boolean } and not to { enabled: false } but that IMHO somewhat complicates the mental model, the return of the investment is uncertain (I'm not 100% sure if that would work) and also it could lead to people ignoring the typegen stuff too often whereas ideally everybody integration package~ should be aware of it and pass it around to not lose the type-safety that it brings.

I could try to prepare changes to jotai that would make this compatible with typegened machines but the easiest way to do that would be to bump a major version of this package... and I somewhat assume that it's not an option right now.

bstro commented 2 years ago

I'm new to Jotai and trying to use with my state machine. Any possible workarounds on this? Maybe I could somehow assert the correct type using some generic exported from Jotai?

edit: I ended up just doing this as a workaround:

export const appMachine = atomWithMachine<typeof initialContext, MachineEvents>(
  (_get) => makeAppMachine() as any
);
jahglow commented 2 years ago

guys please fix it! refer to string types like {typegen: 'enabled'|'disabled'} instead of true | false for boolean