Skip to content

Types Roadmap #61

@devanshj

Description

@devanshj
  • Provide a hack-free safe degraded version of types. Because the currect depend on some likely typescript non-guarantees that might break, So if some users would like to trade safety over precise types and developer experience they can use that version. And we can also be less worried of making workarounds because we have already warned "Hey this might break" :P
    Two ways of going about this -

    • @cassionzen/usestatemachine/safe (temporary name)
    • declare module "@cassionzen/usestatemachine" {
        interface TypeOptions {
          safe: true
        }
      }
      Alternative names to "safe" - workaroundFree, hackFree,
      Current workarounds - InferNarrowestObject which is almost same as this and Definition.FromTypeParameter (TODO: document what's happening here)

    For milestone v1.0.0

  • Support guard's that are actually typed as type guards. Meaning this should work -

    const eventHasFoo = (state: { event: { foo?: string | undefined  } }): state is { event: { foo: string } } =>
      typeof state.event.foo !== "undefined"
    // I always wanted to publish a library provides authoring predicates that infer guards, which I might :P
    // Meaning with that library the above can be replaced by something like
    // let eventHasFoo = P.doesHave("event.foo", P.isNotUndefined)
    // and if they write it in the machine itself (which they should) they'd also get completions and errors via contextual inferrence.
    
    useStateMachine({
      schema: { events: { X: t<{ foo?: string | undefined }> } },
      initial: "a",
      states: {
        a: { on: { X: { target: "b", gaurd: eventHasFoo } },
        b: {
          effect: ({ event }) => {
            let x: string = event.foo
            // without `gaurd: eventHasFoo` foo would have been `string | undefined`
          }
        }
      }
    })

    It's great the signature is ({ context, event }) => ... instead of (context, event) => ... like xstate, because latter is principally incorrect and consequently has some cons

    For milestone v1.1.0

  • Provide (probably opt-in) linting

    • noInitialToDeadStateNodes
      // needs to be written once per (tsconfig) project
      declare module "@cassionzen/usestatemachine" {
        interface TypeOptions {
          noInitialToDeadStateNodes: true
        }
      }
      
      useStateMachine({
        initial: "b", // Error(noInitialToDeadStateNodes): "b" is a dead state node
        states: {
          a: { on: { X: "b" } },
          b: {}
        }
      })
    • noNonExhaustiveEventsSchema -
      useStateMachine({
        schema: {
          events: { // Error(noNonExhaustiveEventsSchema): `$$exhaustive` not set to `true`
            X: t<{ foo: string }>()
          }
        }
      })
    • noSchemalessDefintion
      useStateMachine({ // Error(noSchemalessDefintion): `schema` is not set
        initial: "a",
        states: { a: {} }
      })
    • more? I don't use state machines so I'm not sure what else we could come up with haha

    For milestone v1.2.0

  • more?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions