#Getting a TypeScript union type of possible states

1 messages · Page 1 of 1 (latest)

mortal echo
#

What’s the best way to get a union type that represents all the possible machine states as object path strings? I expose a subset of these as a status value via React context. I can’t find a way to dynamically get this type, but having a separate list of possible values means you need to update two places when you change state names in the machine definition. @jolly flicker?

export const statusValues = [
  'idle',
  'edit.blank',
  'edit.ready',
  'edit.streaming.connecting',
  'edit.streaming.connected',
  'edit.done',
] as const
export type Status = (typeof statusValues)[number]
jolly flicker
#

hm, u should be able to derive this from snapshot.value

#

it depends on how well you know your TS shenanigans ;p

#

i can't write it right now on mobile - if u have problems with this I can give u a hand but i will be mostly offline till Jan 8th

oblique hearth
#

@mortal echo why not define the value you'll assign to the states property externally (with as const) then use type stateUnion = keyof typeof myStates to get the string union?

Then in the machine config,

{
  ...,
  states: myStates,
}
mortal echo
mortal echo
# jolly flicker hm, u should be able to derive this from `snapshot.value`

No problem, I can wait until you’re back! What’s the approach you were going to suggest? We’re dealing with compound states here, and I’m not sure if you can recursively extract types in this way? Keep in mind I want to flatten deeply nested states into strings like edit.streaming.connected.

oblique hearth
#

amazing typescript magic! I just realized we can also get the union of all state representations (not as strings) like this:

type States = Parameters<SnapshotFrom<typeof machine>['matches']>[0];

I imagine the answer at https://stackoverflow.com/questions/47057649/typescript-string-dot-notation-of-nested-object can be adapted to get the dot-separated paths also

mortal echo
# jolly flicker please recheck this but it seems to work

This gets me most of the way there — thanks! I’ll need to tweak this to only allow state paths that include “leaf states”. That is foo.f1 includes a leaf state, but just foo doesn’t and so I don’t want it in my union type.

mortal echo
mortal echo
#

Haha okay, I won’t sue you. Thanks, this is very helpful 🔥

steady seal
#

@jolly flicker for me this solution does not work 🤷‍♂️ I got a typescript error like that -> Type instantiation is excessively deep and possibly infinite.ts(2589). Anyway, this looks like rocket science and all it should do is just return the union of string literals🤦‍♂️ In xstate v4 with typegen retrieving it was as simple as that -> export type States = Typegen0['matchesStates']; and now 🤔

jolly flicker
#

to be fair - typegen metadata wasn't really meant to be consumed publicly ;p

#

if you want me to spend time on looking into your issue you need to provide a repro case

#

i can't investigate what might be happening in your project based on the error message alone

#

this type util might look complicated - because it kinda is - but there is no such thing as "just return the union of strings". You want to compute them somehow so this is the way - it's not like you have to understand in full depth how this type works, just like you don't need to precisely know how XState, React, or whatever else works

#

my point is - it's fine not to understand everything and also... does it matter where the code that you don't understand is located on disk? we have some complicated types in XState already and this one util is not different from them and your are already using those other types because you are using XState, right? 😉

#

it should be simple on ur layer of abstraction in a sense that you want a simple util that receives an input and gives you the output, how it's done is tucked away in a black box

steady seal
#

@jolly flicker There is nothing there which I cannot understand 😀 It is the mix of recursion, mapped types, conditional types, generics etc. By rocket science I meant that you have to write a lot of helper types before you can get what you want which is a simple union of string literals presenting the set of possible state machine states 🤔 I saw the types declaration in xstate as well and no I am wondering if you guys owning the whole library could add a special type like that and just expose it as the other types🤔 As we can see at least some people needs it🤔

jolly flicker
#

yes, we could consider adding it to the core

#

but ultimately - it would be the same type

#

you are merely advocating for a different location of this type in ur project ;p

#

I don't want you to write this type per se - that's why I have shared what I came up with for this. From my PoV, it doesn't matter if you copy-paste it into ur project or if you import it from a package - it's the very same code

#

but before adding things to the library I need to understand the use cases better - and even here we already found out that we don't necessarily think about the same requirements

#

I computed all of the paths with that type - whereas Andy only wanted leafs

steady seal
#

The location of the code and the responsibility to make it work properly will change if it is the internal part of the library right? 😀 I mean that is probably why we use libraries to not write everything ourselves and keep our source code leaner 😛 You could return only to the first level in one type and all levels for example in another type. BTW I do not know why my typescript gives an error if I have version "5.3.3"🤔

jolly flicker
#

i need to see a repro to assist with that