question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to use external machine config with correct type (using TypeScript)?

See original GitHub issue

First of all, thanks for this great little library built for React! The machine syntax is really nice and also the TypeScript support. 👍

My problem is that I want to first define the machine config and then use the config object for the hook. Using your sample machine, it would look like this:

const machine = {
  initial: 'inactive',
  states: {
    inactive: {
      on: { TOGGLE: 'active' },
    },
    active: {
      on: { TOGGLE: 'inactive' },
      effect() {
        console.log('Just entered the Active state');
        // Same cleanup pattern as `useEffect`:
        // If you return a function, it will run when exiting the state.
        return () => console.log('Just Left the Active state');
      },
    },
  },
}

const [state, send] = useStateMachine(machine)

The error that I get is:

Argument of type '{ initial: string; states: { inactive: { on: { TOGGLE: string; }; }; active: { on: { TOGGLE: string; }; effect(): () => void; }; }; }' is not assignable to parameter of type 'InferNarrowestObject<Definition<{ initial: string; states: { inactive: { on: { TOGGLE: string; }; }; active: { on: { TOGGLE: string; }; effect(): () => void; }; }; }, { inactive: { on: { TOGGLE: string; }; }; active: { on: { TOGGLE: string; }; effect(): () => void; }; }, undefined, false>>'.
  Types of property 'initial' are incompatible.
    Type 'string' is not assignable to type '"inactive" | "active"'.ts(2345)

I tries the following types without any success:

const machine1: Machine.Definition.Impl = { /* */ }
const machine2: Machine.Definition<unknown, { inactive: 'inactive'; active: 'active' }> = { /* */ }

I also tried to get around the error by definition my own type. But it is incomplete and I don’t want to redefine something that already exists somewhere else.

  interface IMachine {
    initial: string
    states: {
      [key: string]: {
        on: { [key: string]: string | { target: string /*guard?: any*/ } }
        effect?: () => void // works, but misses the arguments...
      }
    }  
  }

🤷🏻 How do I solve this problem in a clean way with defining my own types? They are already there. I just cannot figure how to apply them correctly.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
devanshjcommented, Sep 1, 2021

Thanks for reporting you’re providing really useful feedback!

It’s a known typescript limitation (I forgot about this) that effects become unknown (when using parameters) after you pass the definition to createDefinition. One workaround is to replace effect: unknown with effect: any you can use this updated createDefinition

import { Machine, A } from '@cassiozen/usestatemachine/dist/types'

const createDefinition = <D extends Machine.Definition<D>>(definition: A.InferNarrowestObject<D>) =>
  definition as MapEffectsToAny<D extends Machine.Definition<infer X> ? X : never>

type MapEffectsToAny<T> =
  T extends object
    ? { [K in keyof T]: K extends "effect" ? any : MapEffectsToAny<T[K]> }
    : T

Now your snippet compiles. Once we publish a version with createDefinition it’s going to be easy to write definitions outside.

Let me know if the issue still persists in someway.

0reactions
devanshjcommented, Sep 28, 2021

Yeah that’s problematic, #79 will solve this. And thanks for the feedback!

Read more comments on GitHub >

github_iconTop Results From Across the Web

TSConfig Reference - Docs on every TSConfig option
Compiler Options. These options make up the bulk of TypeScript's configuration and it covers how the language should work. Type Checking; Modules; Emit ......
Read more >
Using TypeScript | XState Docs
You can use the generated types to specify the return type of promise-based services, ... The generic types for MachineConfig<TContext, any, ...
Read more >
How To Create Custom Types in TypeScript - DigitalOcean
This will allow you to create types that check specific string formats and add more customization to your TypeScript project.
Read more >
CRA Typescript - Read in config file with post deployment values
The general suggestion is to put a config.js in the public folder, reference it as a script in index.html and finally call it...
Read more >
A Complete Guide to Using TypeScript in Node.js - Better Stack
We need to set up a configuration file ( tsconfig.json ) for our project before we can start compiling our source files. If...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found