statelyai / xstate

Actor-based state management & orchestration for complex app logic.
https://stately.ai/docs
MIT License
27.18k stars 1.26k forks source link

Bug: Schemata + Typegen break string literals, event suggestions, and `ActorRef` assignments #3028

Closed christoph-fricke closed 2 years ago

christoph-fricke commented 2 years ago

Description

I noticed a few problem with schemata + typegen:

  1. String literals unions (e.g. "working" | "not-working") used in context are broadened to string, breaking selectors and other potential code that requires the string literal union.
  2. Since 4.29 the result of spawning a machine is no longer assignable to an actor ref created with ActorRefFrom, which makes it nearly impossible to type the spawned machine for passing it around.
  3. While preparing the code sandbox, I noticed that VS Code does not infer event types in the machine definition when schemata are used.

Expected result

  1. String literals unions should be typed as string literal unions and not string.
  2. Spawned machines are assignable to their corresponding ActorRef.
  3. VS Code should suggest event types in <state>.on like it does when events are specified through the generic.

Actual result

  1. Instead of preserving a literal union, the type is broadened to string.
  2. A nasty TypeScript error appears.
  3. VS Code only suggests "" and "*", not the actual event types.

Reproduction

https://codesandbox.io/s/floral-bush-yxn10?file=/src/index.ts

Additional context

In the sandbox, schemas are disabled by default and can be enabled by removing the comments (createMachine generics have to be removed as well).

XState Version: 4.29.0

jonnyirwin commented 2 years ago

I started changing my codebase over to typegen yesterday and was also struggling with typing errors on my ActorRefFrom. I assumed it was me missing something.

Andarist commented 2 years ago

@christoph-fricke could you split this into 3 issues? being able to track those separately would make it easier to work with those.

For now, I've looked into the string literal case and I'm not yet sure what we can do about avoiding breaking changes. I already had a refactor plan for v5 that would just avoid this altogether but it's not an easy decision to use that strategy in v4. Potentially this could be fixed with overloads but overloads have their own set of problems that I'd like to avoid. We also could use NoInfer trick but that would break people relying on inference and using config.context to type this (instead of config.schema.context).

I've put together a simplified repro case for this here

jonnyirwin commented 2 years ago

I know this is going to be split into 3 separate issues, but looking at the issue withActorRefFrom, previous to 4.29.0, using type ChildActor = ActorRefFrom<typeof createChild>; i.e. typing on the factory function (from @christoph-fricke codesandbox https://codesandbox.io/s/floral-bush-yxn10?file=/src/index.ts ), would have worked. I think it now works changing to be typed like this: type ChildActor = ActorRefFrom<ReturnType<typeof createChild>>; i.e. on the machine, rather than the factory function.

Andarist commented 2 years ago

Issues 1 and 3 are both being fixed by https://github.com/statelyai/xstate/pull/3084

For the second issue, I've created a minimal repro case here and I have a fix locally but I need to think about it more cause I don't fully understand the relationship between those types

Andarist commented 2 years ago

Fix for the second issue is up here: https://github.com/statelyai/xstate/pull/3087