@@ -2055,10 +2055,10 @@ export declare namespace Machine {
20552055 *
20562056 * **Details**
20572057 *
2058- * Event handlers may declare an upper bound of possible target paths. A
2059- * handler without that declaration is explicitly reported as dynamic. The
2060- * source, trigger, and reentry behavior are available without executing the
2061- * handler.
2058+ * Event, eventless, and completion handlers may declare an upper bound of
2059+ * possible target paths. A handler without that declaration is explicitly
2060+ * reported as dynamic. The source, trigger, and reentry behavior are
2061+ * available without executing the handler.
20622062 *
20632063 * @category models
20642064 * @since 4.0.0
@@ -3417,7 +3417,7 @@ export declare namespace Machine {
34173417 * @since 4.0.0
34183418 */
34193419 export type AlwaysReturn < Config > = Config extends { readonly always ?: infer Always }
3420- ? NonNullable < Always > extends ( ... args : any ) => infer Ret ? Ret : never
3420+ ? EventTransitionReturn < NonNullable < Always > >
34213421 : never
34223422 /**
34233423 * Extracts the return value from a state completion transition.
@@ -3426,7 +3426,7 @@ export declare namespace Machine {
34263426 * @since 4.0.0
34273427 */
34283428 export type DoneReturn < Config > = Config extends { readonly onDone ?: infer OnDone }
3429- ? NonNullable < OnDone > extends ( ... args : any ) => infer Ret ? Ret : never
3429+ ? EventTransitionReturn < NonNullable < OnDone > >
34303430 : never
34313431 /**
34323432 * Extracts the return value from a final state output function.
@@ -3596,8 +3596,24 @@ export declare namespace Machine {
35963596 readonly entry ?: ( context : StateActionContext < States , Events , Emits , StateId > ) => StateActionResult < any , any >
35973597 readonly exit ?: ( context : StateActionContext < States , Events , Emits , StateId > ) => StateActionResult < any , any >
35983598 readonly invoke ?: InvokeDefinition < States , Events , Emits , StateId >
3599- readonly always ?: ( context : AlwaysContext < States , Events , Emits , StateId > ) => HandlerResult < States , any , any >
3600- readonly onDone ?: ( context : DoneContext < States , Events , Emits , StateId > ) => HandlerResult < States , any , any >
3599+ readonly always ?:
3600+ | ( ( context : AlwaysContext < States , Events , Emits , StateId > ) => HandlerResult < States , any , any > )
3601+ | {
3602+ /** Statically declared upper bound of possible target paths. */
3603+ readonly targets ?: ReadonlyArray < StateNodeIdentifier < States > >
3604+ readonly transition : (
3605+ context : AlwaysContext < States , Events , Emits , StateId >
3606+ ) => HandlerResult < States , any , any >
3607+ }
3608+ readonly onDone ?:
3609+ | ( ( context : DoneContext < States , Events , Emits , StateId > ) => HandlerResult < States , any , any > )
3610+ | {
3611+ /** Statically declared upper bound of possible target paths. */
3612+ readonly targets ?: ReadonlyArray < StateNodeIdentifier < States > >
3613+ readonly transition : (
3614+ context : DoneContext < States , Events , Emits , StateId >
3615+ ) => HandlerResult < States , any , any >
3616+ }
36013617 readonly on ?: {
36023618 readonly [ EventTag in TagOf < Events [ number ] > ] ?:
36033619 | ( (
@@ -3935,6 +3951,22 @@ export declare namespace Machine {
39353951 : unknown
39363952 : unknown
39373953
3954+ type HandlerDirectTargetValidation <
3955+ StateId extends string ,
3956+ Config ,
3957+ Trigger extends "always" | "onDone" ,
3958+ Transition = Config extends { readonly [ Key in Trigger ] ?: infer Value } ? NonNullable < Value > : never ,
3959+ Undeclared extends string = UndeclaredTransitionTarget < Transition >
3960+ > = Trigger extends keyof Config ? [ Undeclared ] extends [ never ] ? unknown
3961+ : {
3962+ readonly [ Key in Trigger ] : HandlerValidationError <
3963+ "Transition returns a target not listed in targets" ,
3964+ StateId ,
3965+ readonly [ trigger : Trigger , target : Undeclared ]
3966+ >
3967+ }
3968+ : unknown
3969+
39383970 type HandlerDepth = readonly [ unknown , unknown , unknown , unknown , unknown , unknown , unknown , unknown ]
39393971
39403972 type HandlerNextDepth < Depth extends ReadonlyArray < unknown > > = Depth extends
@@ -4024,6 +4056,8 @@ export declare namespace Machine {
40244056 & HandlerUnknownConfigKeyValidation < StateId , Config >
40254057 & HandlerOnKeyValidation < Events , StateId , Config >
40264058 & HandlerOnTargetValidation < StateId , Config >
4059+ & HandlerDirectTargetValidation < StateId , Config , "always" >
4060+ & HandlerDirectTargetValidation < StateId , Config , "onDone" >
40274061 & HandlerInvokeOutputValidation < Events , StateId , Config >
40284062 & HandlerInvokeEmitsValidation < Events , StateId , Config >
40294063 & HandlerInvokeSnapshotValidation < Events , StateId , Config >
@@ -4494,8 +4528,22 @@ export declare namespace Machine {
44944528 readonly entry ?: ( context : StateActionContext < States , Events , Emits , StateId > ) => StateActionResult < E , R >
44954529 readonly exit ?: ( context : StateActionContext < States , Events , Emits , StateId > ) => StateActionResult < E , R >
44964530 readonly invoke ?: InvokeDefinition < States , Events , Emits , StateId >
4497- readonly always ?: ( context : AlwaysContext < States , Events , Emits , StateId > ) => HandlerResult < States , E , R >
4498- readonly onDone ?: ( context : DoneContext < States , Events , Emits , StateId > ) => HandlerResult < States , E , R >
4531+ readonly always ?:
4532+ | ( ( context : AlwaysContext < States , Events , Emits , StateId > ) => HandlerResult < States , E , R > )
4533+ | {
4534+ readonly targets ?: ReadonlyArray < StateNodeIdentifier < States > >
4535+ readonly transition : (
4536+ context : AlwaysContext < States , Events , Emits , StateId >
4537+ ) => HandlerResult < States , E , R >
4538+ }
4539+ readonly onDone ?:
4540+ | ( ( context : DoneContext < States , Events , Emits , StateId > ) => HandlerResult < States , E , R > )
4541+ | {
4542+ readonly targets ?: ReadonlyArray < StateNodeIdentifier < States > >
4543+ readonly transition : (
4544+ context : DoneContext < States , Events , Emits , StateId >
4545+ ) => HandlerResult < States , E , R >
4546+ }
44994547 readonly output ?:
45004548 | ( ( context : FinalOutputContext < States , Events , StateId > ) => unknown )
45014549 | ( ( context : ParallelOutputContext < States , Events , StateId > ) => unknown )
@@ -4550,6 +4598,29 @@ const cloneWithHandlers = (
45504598 return machine
45514599}
45524600
4601+ const validateTransitionTargets = (
4602+ stateNodes : Machine . StateNodes ,
4603+ path : string ,
4604+ trigger : PropertyKey ,
4605+ transition : unknown
4606+ ) : void => {
4607+ if ( typeof transition !== "object" || transition === null || ! hasProperty ( transition , "targets" ) ) {
4608+ return
4609+ }
4610+ if ( ! Array . isArray ( transition . targets ) ) {
4611+ throw new Error (
4612+ `Machine expected transition targets for state "${ path } " on "${ String ( trigger ) } " to be an array`
4613+ )
4614+ }
4615+ for ( const target of transition . targets ) {
4616+ if ( typeof target !== "string" || ! stateNodes . byPath . has ( target ) ) {
4617+ throw new Error (
4618+ `Machine transition for state "${ path } " on "${ String ( trigger ) } " declares unknown target "${ String ( target ) } "`
4619+ )
4620+ }
4621+ }
4622+ }
4623+
45534624const flattenHandlers = (
45544625 handlers : Record < PropertyKey , Machine . AnyStateConfig > ,
45554626 stateNodes : Machine . StateNodes ,
@@ -4570,24 +4641,11 @@ const flattenHandlers = (
45704641 const on = stateConfig . on
45714642 if ( typeof on === "object" && on !== null ) {
45724643 for ( const event of Reflect . ownKeys ( on ) ) {
4573- const transition = ( on as Record < PropertyKey , unknown > ) [ event ]
4574- if ( typeof transition !== "object" || transition === null || ! hasProperty ( transition , "targets" ) ) {
4575- continue
4576- }
4577- if ( ! Array . isArray ( transition . targets ) ) {
4578- throw new Error (
4579- `Machine expected transition targets for state "${ path } " on "${ String ( event ) } " to be an array`
4580- )
4581- }
4582- for ( const target of transition . targets ) {
4583- if ( typeof target !== "string" || ! stateNodes . byPath . has ( target ) ) {
4584- throw new Error (
4585- `Machine transition for state "${ path } " on "${ String ( event ) } " declares unknown target "${ String ( target ) } "`
4586- )
4587- }
4588- }
4644+ validateTransitionTargets ( stateNodes , path , event , ( on as Record < PropertyKey , unknown > ) [ event ] )
45894645 }
45904646 }
4647+ validateTransitionTargets ( stateNodes , path , "always" , stateConfig . always )
4648+ validateTransitionTargets ( stateNodes , path , "done" , stateConfig . onDone )
45914649 handlers [ path ] = stateConfig as Machine . AnyStateConfig
45924650 if ( childConfig !== undefined ) {
45934651 const node = Model . getStateNodeDefinition ( path , states [ key ] )
@@ -5978,8 +6036,9 @@ export const stateNodes = <M extends Machine.Any>(
59786036 *
59796037 * Event handlers retain their handler-key order within each source state and
59806038 * are followed by eventless and completion handlers. This function does not
5981- * execute handlers. Event handlers with a `targets` declaration expose those
5982- * possible paths; handlers without one remain dynamic.
6039+ * execute handlers. Object-form event, eventless, and completion handlers with
6040+ * a `targets` declaration expose those possible paths; handlers without one
6041+ * remain dynamic.
59836042 *
59846043 * @category getters
59856044 * @since 4.0.0
0 commit comments