diff --git a/.changeset/tasty-chicken-itch.md b/.changeset/tasty-chicken-itch.md new file mode 100644 index 0000000000..91c5aea351 --- /dev/null +++ b/.changeset/tasty-chicken-itch.md @@ -0,0 +1,5 @@ +--- +'xstate': patch +--- + +Fixed an issue with inline functions in the config object used as transition actions not having their argument types inferred. diff --git a/packages/core/src/StateNode.ts b/packages/core/src/StateNode.ts index 32047ca815..a31a3a5db4 100644 --- a/packages/core/src/StateNode.ts +++ b/packages/core/src/StateNode.ts @@ -66,7 +66,6 @@ import { InvokeSourceDefinition, MachineSchema, ActorRef, - StateMachine, InternalMachineOptions, ServiceMap, StateConfig, @@ -306,7 +305,7 @@ class StateNode< private _context: | Readonly | (() => Readonly) = ('context' in config - ? (config as StateMachine).context + ? (config as any).context : undefined) as any, // TODO: this is unsafe, but we're removing it in v5 anyway _stateInfo?: { parent: StateNode; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 6395246cae..4114a28187 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -449,14 +449,12 @@ export type TransitionConfigOrTarget< >; export type TransitionsConfigMap = { - [K in TEvent['type']]?: TransitionConfigOrTarget< - TContext, - TEvent extends { type: K } ? TEvent : never - >; -} & { - ''?: TransitionConfigOrTarget; -} & { - '*'?: TransitionConfigOrTarget; + [K in TEvent['type'] | '' | '*']?: K extends '' | '*' + ? TransitionConfigOrTarget + : TransitionConfigOrTarget< + TContext, + TEvent extends { type: K } ? TEvent : never + >; }; type TransitionsConfigArray = Array< diff --git a/packages/core/test/types.test.ts b/packages/core/test/types.test.ts index 33557207f1..7489dd6a5e 100644 --- a/packages/core/test/types.test.ts +++ b/packages/core/test/types.test.ts @@ -514,6 +514,57 @@ describe('events', () => { acceptMachine(toggleMachine); }); + + it('should infer inline function parameters when narrowing transition actions based on the event type', () => { + createMachine({ + schema: { + context: {} as { + count: number; + }, + events: {} as + | { type: 'EVENT_WITH_FLAG'; flag: boolean } + | { + type: 'EVENT_WITHOUT_FLAG'; + } + }, + on: { + EVENT_WITH_FLAG: { + actions: (_context, event) => { + ((_accept: 'EVENT_WITH_FLAG') => {})(event.type); + ((_accept: boolean) => {})(event.flag); + // @ts-expect-error + ((_accept: 'is not any') => {})(event); + } + } + } + }); + }); + + it('should infer inline function parameters when for a wildcard transition', () => { + createMachine({ + schema: { + context: {} as { + count: number; + }, + events: {} as + | { type: 'EVENT_WITH_FLAG'; flag: boolean } + | { + type: 'EVENT_WITHOUT_FLAG'; + } + }, + on: { + '*': { + actions: (_context, event) => { + ((_accept: 'EVENT_WITH_FLAG' | 'EVENT_WITHOUT_FLAG') => {})( + event.type + ); + // @ts-expect-error + ((_accept: 'is not any') => {})(event); + } + } + } + }); + }); }); describe('interpreter', () => {