Releases: statelyai/xstate
@xstate/solid@0.2.1
Patch Changes
-
#5055
ad38c35c37
Thanks @SandroMaglione! - Updated types ofuseActor
,useMachine
, anduseActorRef
to requireinput
when defined insidetypes/input
.Previously even when
input
was defined insidetypes
,useActor
,useMachine
, anduseActorRef
would not make the input required:const machine = setup({ types: { input: {} as { value: number } } }).createMachine({}); function App() { // Event if `input` is not defined, `useMachine` works at compile time, but risks crashing at runtime const _ = useMachine(machine); return <></>; }
With this change the above code will show a type error, since
input
is now required:const machine = setup({ types: { input: {} as { value: number } } }).createMachine({}); function App() { const _ = useMachine(machine, { input: { value: 1 } // Now input is required at compile time! }); return <></>; }
This avoids runtime errors when forgetting to pass
input
when defined insidetypes
.
@xstate/react@4.1.2
Patch Changes
-
#5055
ad38c35c37
Thanks @SandroMaglione! - Updated types ofuseActor
,useMachine
, anduseActorRef
to requireinput
when defined insidetypes/input
.Previously even when
input
was defined insidetypes
,useActor
,useMachine
, anduseActorRef
would not make the input required:const machine = setup({ types: { input: {} as { value: number } } }).createMachine({}); function App() { // Event if `input` is not defined, `useMachine` works at compile time, but risks crashing at runtime const _ = useMachine(machine); return <></>; }
With this change the above code will show a type error, since
input
is now required:const machine = setup({ types: { input: {} as { value: number } } }).createMachine({}); function App() { const _ = useMachine(machine, { input: { value: 1 } // Now input is required at compile time! }); return <></>; }
This avoids runtime errors when forgetting to pass
input
when defined insidetypes
.
xstate@5.18.0
Minor Changes
-
#5042
54c9d9e6a4
Thanks @boneskull! -waitFor()
now accepts a{signal: AbortSignal}
inWaitForOptions
-
#5006
1ab974547f
Thanks @davidkpiano! - The state value typings for setup state machine actors (setup({}).createMachine({ ... })
) have been improved to represent the actual expected state values.const machine = setup({}).createMachine({ initial: 'green', states: { green: {}, yellow: {}, red: { initial: 'walk', states: { walk: {}, wait: {}, stop: {} } }, emergency: { type: 'parallel', states: { main: { initial: 'blinking', states: { blinking: {} } }, cross: { initial: 'blinking', states: { blinking: {} } } } } } }); const actor = createActor(machine).start(); const stateValue = actor.getSnapshot().value; if (stateValue === 'green') { // ... } else if (stateValue === 'yellow') { // ... } else if ('red' in stateValue) { stateValue; // { // red: "walk" | "wait" | "stop"; // } } else { stateValue; // { // emergency: { // main: "blinking"; // cross: "blinking"; // }; // } }
Patch Changes
-
#5054
853f6daa0b
Thanks @davidkpiano! - TheCallbackLogicFunction
type (previouslyInvokeCallback
) is now exported. This is the callback function that you pass intofromCallback(callbackLogicFn)
to create an actor from a callback function.import { type CallbackLogicFunction } from 'xstate'; // ...
@xstate/store@2.3.0
Minor Changes
-
#5056
8c35da9a72
Thanks @steveadams! - You can now use the xstate/store package with SolidJS.Import
useSelector
from@xstate/store/solid
. Select the data you want viauseSelector(…)
and send events usingstore.send(eventObject)
:import { donutStore } from './donutStore.ts'; import { useSelector } from '@xstate/store/solid'; function DonutCounter() { const donutCount = useSelector(donutStore, (state) => state.context.donuts); return ( <div> <button onClick={() => donutStore.send({ type: 'addDonut' })}> Add donut ({donutCount()}) </button> </div> ); }
@xstate/store@2.2.1
Patch Changes
b740aafdb1
Thanks @davidkpiano! - Fixed some small issues from #5027 regarding XState types being imported
xstate@5.17.4
Patch Changes
- #5039
d6df8fb470
Thanks @Andarist! - Fixed an inference issue that preventedemit
used directly insetup
(or barecreateMachine
) to benefit fromtypes.emitted
types.
@xstate/store@2.2.0
Minor Changes
-
#5027
758a78711d
Thanks @davidkpiano! - You can now inspect XState stores using the.inspect(inspector)
method:import { someStore } from './someStore'; someStore.inspect((inspEv) => { console.log(inspEv); // logs "@xstate.event" events and "@xstate.snapshot" events // whenever an event is sent to the store }); // The "@xstate.actor" event is immediately logged
xstate@5.17.3
Patch Changes
- #5034
7bed484c38
Thanks @davidkpiano! - FixEventFrom
andContextFrom
types
xstate@5.17.2
Patch Changes
-
#5029
88bd87ab41
Thanks @davidkpiano! - RevertActorRefFrom
change -
#5011
a275d274de
Thanks @davidkpiano! - There is a new type helper:ActorRefFromLogic<TLogic>
. This type is a stricter form ofActorRefFrom<TLogic>
that only accepts actor logic types. See #4997 for more details.
@xstate/store@2.1.0
Minor Changes
-
#5020
e974797b0
Thanks @with-heart! - Added theEventFromStore
utility type which extracts the type of events from a store:import { createStore, type EventFromStore } from '@xstate/store'; const store = createStore( { count: 0 }, { add: (context, event: { addend: number }) => ({ count: context.count + event.addend }), multiply: (context, event: { multiplier: number }) => ({ count: context.count * event.multiplier }) } ); type StoreEvent = EventFromStore<typeof store>; // ^? { type: 'add'; addend: number } | { type: 'multiply'; multiplier: number }
EventFromStore
allows us to create our own utility types which operate on a store's event types.For example, we could create a type
EventByType
which extracts the specific type of store event whereType
matches the event'stype
property:import { type EventFromStore, type Store } from '@xstate/store'; /** * Extract the event where `Type` matches the event's `type` from the given * `Store`. */ type EventByType< TStore extends Store<any, any>, // creates a type-safe relationship between `Type` and the `type` keys of the // store's events Type extends EventFromStore<TStore>['type'] > = Extract<EventFromStore<TStore>, { type: Type }>;
Here's how the type works with the
store
we defined in the first example:// we get autocomplete listing the store's event `type` values on the second // type parameter type AddEvent = EventByType<typeof store, 'add'>; // ^? { type: 'add'; addend: number } type MultiplyEvent = EventByType<typeof store, 'multiply'>; // ^? { type: 'multiply'; multiplier: number } // the second type parameter is type-safe, meaning we get a type error if the // value isn't a valid event `type` type DivideEvent = EventByType<typeof store, 'divide'>; // Type '"divide"' does not satisfy the constraint '"add" | "multiply"'.ts(2344)
Building on that, we could create a type
EventInputByType
to extract a specific event's "input" type (the event type without thetype
property):import { type EventFromStore, type Store } from '@xstate/store'; /** * Extract a specific store event's "input" type (the event type without the * `type` property). */ type EventInputByType< TStore extends Store<any, any>, Type extends EventFromStore<TStore>['type'] > = Omit<EventByType<TStore, Type>, 'type'>;
And here's how
EventInputByType
works with our examplestore
:type AddInput = EventInputByType<typeof store, 'add'>; // ^? { addend: number } type MultiplyInput = EventInputByType<typeof store, 'multiply'>; // ^? { multiplier: number } type DivideInput = EventInputByType<typeof store, 'divide'>; // Type '"divide"' does not satisfy the constraint '"add" | "multiply"'.ts(2344)
Putting it all together, we can use
EventInputByType
to create a type-safe transition function for each of our store's defined events:import { createStore, type EventFromStore, type Store } from '@xstate/store'; /** * Extract the event where `Type` matches the event's `type` from the given * `Store`. */ type EventByType< TStore extends Store<any, any>, Type extends EventFromStore<TStore>['type'] > = Extract<EventFromStore<TStore>, { type: Type }>; /** * Extract a specific store event's "input" type (the event type without the * `type` property). */ type EventInputByType< TStore extends Store<any, any>, Type extends EventFromStore<TStore>['type'] > = Omit<EventByType<TStore, Type>, 'type'>; const store = createStore( { count: 0 }, { add: (context, event: { addend: number }) => ({ count: context.count + event.addend }), multiply: (context, event: { multiplier: number }) => ({ count: context.count * event.multiplier }) } ); const add = (input: EventInputByType<typeof store, 'add'>) => store.send({ type: 'add', addend: input.addend }); add({ addend: 1 }); // sends { type: 'add', addend: 1 } const multiply = (input: EventInputByType<typeof store, 'multiply'>) => store.send({ type: 'multiply', multiplier: input.multiplier }); multiply({ multiplier: 2 }); // sends { type: 'multiply', multiplier: 2 }
Happy typing!