matthewp / robot

🤖 A functional, immutable Finite State Machine library

Home Page:https://thisrobot.life

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Typescript issue accessing the child in a nested scenario

phtrivier opened this issue · comments

I'm trying to run the scenario from the "Nested Guide" test, with Typescript.

Copying the exemple, I get some errors:

  1. I need to provide a context when calling createMachine, otherwise the compiler complains with:
   robot3.spec.ts:258:29 - error TS2345: Argument of type 'Machine<{ green: MachineState; yellow: MachineState; red: MachineState; }, unknown, "yellow" | "green" | "red">' is not assignable to parameter of type 'Machine<{}, {}, string>'.
      Type 'unknown' is not assignable to type '{}'.

    258     let service = interpret(stoplight, () => {});
  1. The compiler does not let me access the child by getting using the service.send function:
let child = service.send;
console.log(child.machine.current); // walk
   robot3.spec.ts:267:23 - error TS2339: Property 'machine' does not exist on type 'SendFunction<string>'.

    267     console.log(child.machine.current); // walk
                              ~~~~~~~
    robot3.spec.ts:269:11 - error TS2339: Property 'send' does not exist on type 'SendFunction<string>'.

    269     child.send('toggle');

However, I suspect this might be a typo in the Guide, since the child machine seems to be accessible at runtime by doing this:

let child = service.child;
console.log(child.machine.current); // walk

Is it a problem with the documentation ?

  1. However, in this case, the Service typescript type does not seem to have the child property:
robot3.spec.ts:267:23 - error TS2339: Property 'machine' does not exist on type 'SendFunction<string>'.

    267     console.log(child.machine.current); // walk
                              ~~~~~~~
  1. In the end, I can run the test successfully doing this:
it("can nest machines", async () => {

    const stopwalk = createMachine({
      walk: state(
        transition('toggle', 'dontWalk')
      ),
      dontWalk: state(
        transition('toggle', 'walk')
      )
    }, () : any => {} );
    
    const stoplight = createMachine({
      green: state(
        transition('next', 'yellow')
      ),
      yellow: state(
        transition('next', 'red')
      ),
      red: invoke(stopwalk,
        transition('done', 'green')
      )
    },() : any => {}); // Required "context" for typing

    let service = interpret(stoplight, () => {});

    service.send('next');
    console.log(service.machine.current); // yellow
    
    service.send('next');
    console.log(service.machine.current); // red
    
    let child = (service as any).child; // Need to cast, and access child instead of send
    console.log(child.machine.current); // walk
    
    child.send('toggle');
    console.log(child.machine.current); // dontWalk
    console.log(service.machine.current); // green

  })

Is there a cleaner way ?

Thanks !

Looks like our typings don't account for service.child which gets added by invoke(): https://github.com/matthewp/robot/blob/master/index.d.ts Want to add it?