diff --git a/packages/core/src/State.ts b/packages/core/src/State.ts index d688a7fa0e5..aea08995abc 100644 --- a/packages/core/src/State.ts +++ b/packages/core/src/State.ts @@ -70,6 +70,7 @@ export class State< TContext extends MachineContext, TEvent extends EventObject, TActor extends ProvidedActor, + TTag extends string, TOutput, TResolvedTypesMeta = TypegenDisabled > { @@ -106,13 +107,29 @@ export class State< TContext extends MachineContext, TEvent extends EventObject = EventObject >( - stateValue: State | StateValue, + stateValue: + | State< + TContext, + TEvent, + TODO, + any, // tags + any, // output + any // typege + > + | StateValue, context: TContext = {} as TContext, machine: AnyStateMachine - ): State { + ): State< + TContext, + TEvent, + TODO, + any, // tags + any, // output + any // typegen + > { if (stateValue instanceof State) { if (stateValue.context !== context) { - return new State( + return new State( { value: stateValue.value, context, @@ -131,7 +148,7 @@ export class State< getStateNodes(machine.root, stateValue) ); - return new State( + return new State( { value: stateValue, context, @@ -212,7 +229,7 @@ export class State< public hasTag( tag: TResolvedTypesMeta extends TypegenEnabled ? Prop, 'tags'> - : string + : TTag ): boolean { return this.tags.has(tag as string); } diff --git a/packages/core/src/StateMachine.ts b/packages/core/src/StateMachine.ts index 6e2a0b861ad..847269ee4e9 100644 --- a/packages/core/src/StateMachine.ts +++ b/packages/core/src/StateMachine.ts @@ -71,10 +71,10 @@ export class StateMachine< > implements ActorLogic< TEvent, - State, - State, + State, + State, PersistedMachineState< - State + State >, TODO, TInput, @@ -204,7 +204,7 @@ export class StateMachine< * @param state The state to resolve */ public resolveState( - state: State + state: State ): typeof state { const configurationSet = getConfiguration( getStateNodes(this.root, state.value) @@ -223,7 +223,7 @@ export class StateMachine< ...[context]: Equals extends true ? [] : [TContext] - ): State { + ): State { const resolvedStateValue = resolveStateValue(this.root, stateValue); return this.resolveState(State.from(resolvedStateValue, context, this)); @@ -237,10 +237,10 @@ export class StateMachine< * @param event The received event */ public transition( - state: State, + state: State, event: TEvent, actorCtx: ActorContext - ): State { + ): State { // TODO: handle error events in a better way if ( isErrorEvent(event) && @@ -264,15 +264,15 @@ export class StateMachine< * @param event The received event */ public microstep( - state: State, + state: State, event: TEvent, actorCtx: AnyActorContext - ): Array> { + ): Array> { return macrostep(state, event, actorCtx).microstates; } public getTransitionData( - state: State, + state: State, event: TEvent ): Array> { return transitionNode(this.root, state.value, state, event) || []; @@ -285,7 +285,7 @@ export class StateMachine< private getPreInitialState( actorCtx: AnyActorContext, initEvent: any - ): State { + ): State { const { context } = this.config; const preInitial = this.resolveState( @@ -319,10 +319,10 @@ export class StateMachine< public getInitialState( actorCtx: ActorContext< TEvent, - State + State >, input?: TInput - ): State { + ): State { const initEvent = createInitEvent(input) as unknown as TEvent; // TODO: fix; const preInitialState = this.getPreInitialState(actorCtx, initEvent); @@ -353,7 +353,7 @@ export class StateMachine< } public start( - state: State + state: State ): void { Object.values(state.children).forEach((child: any) => { if (child.status === 0) { @@ -387,25 +387,25 @@ export class StateMachine< } public getPersistedState( - state: State + state: State ): PersistedMachineState< - State + State > { return getPersistedState(state); } public createState( stateConfig: - | State + | State | StateConfig - ): State { + ): State { return stateConfig instanceof State ? stateConfig : new State(stateConfig, this); } public getStatus( - state: State + state: State ) { return state.error ? { status: 'error', data: state.error } @@ -416,13 +416,13 @@ export class StateMachine< public restoreState( state: PersistedMachineState< - State + State >, _actorCtx: ActorContext< TEvent, - State + State > - ): State { + ): State { const children: Record = {}; Object.keys(state.children).forEach((actorId) => { @@ -452,6 +452,7 @@ export class StateMachine< TContext, TEvent, TActor, + TTag, TOutput, TResolvedTypesMeta > = this.createState(new State({ ...state, children }, this)); diff --git a/packages/core/src/StateNode.ts b/packages/core/src/StateNode.ts index 5b1f2323af7..e1a34fcaf8a 100644 --- a/packages/core/src/StateNode.ts +++ b/packages/core/src/StateNode.ts @@ -372,7 +372,7 @@ export class StateNode< } public next( - state: State, + state: State, event: TEvent ): TransitionDefinition[] | undefined { const eventType = event.type; diff --git a/packages/core/src/stateUtils.ts b/packages/core/src/stateUtils.ts index 4bb9942c6ae..f476c173642 100644 --- a/packages/core/src/stateUtils.ts +++ b/packages/core/src/stateUtils.ts @@ -670,7 +670,7 @@ export function getStateNodes< TEvent extends EventObject >( stateNode: AnyStateNode, - state: StateValue | State + state: StateValue | State ): Array { const stateValue = state instanceof State ? state.value : toStateValue(state); @@ -706,7 +706,7 @@ export function transitionAtomicNode< >( stateNode: AnyStateNode, stateValue: string, - state: State, + state: State, event: TEvent ): Array> | undefined { const childStateNode = getStateNode(stateNode, stateValue); @@ -725,7 +725,7 @@ export function transitionCompoundNode< >( stateNode: AnyStateNode, stateValue: StateValueMap, - state: State, + state: State, event: TEvent ): Array> | undefined { const subStateKeys = Object.keys(stateValue); @@ -751,7 +751,7 @@ export function transitionParallelNode< >( stateNode: AnyStateNode, stateValue: StateValueMap, - state: State, + state: State, event: TEvent ): Array> | undefined { const allInnerTransitions: Array> = []; @@ -787,7 +787,14 @@ export function transitionNode< >( stateNode: AnyStateNode, stateValue: StateValue, - state: State, + state: State< + TContext, + TEvent, + TODO, + TODO, + TODO, // output + TODO // tags + >, event: TEvent ): Array> | undefined { // leaf node diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index c449fbe1dd5..9f71c68fae4 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -784,7 +784,14 @@ export type AnyStateNode = StateNode; export type AnyStateNodeDefinition = StateNodeDefinition; -export type AnyState = State; +export type AnyState = State< + any, + any, + any, + any, + any, // delays + any // tags +>; export type AnyStateMachine = StateMachine< any, @@ -1663,7 +1670,7 @@ export type ActorRefFrom = ReturnTypeOrValue extends infer R infer _TAction, infer _TGuard, infer _TDelay, - infer _TTag, + infer TTag, infer _TInput, infer TOutput, infer TResolvedTypesMeta @@ -1674,6 +1681,7 @@ export type ActorRefFrom = ReturnTypeOrValue extends infer R TContext, TEvent, TActor, + TTag, TOutput, AreAllImplementationsAssumedToBeProvided extends false ? MarkAllImplementationsAsProvided @@ -1709,7 +1717,7 @@ export type InterpreterFrom< infer _TAction, infer _TGuard, infer _TDelay, - infer _TTag, + infer TTag, infer TInput, infer TOutput, infer TResolvedTypesMeta @@ -1717,10 +1725,10 @@ export type InterpreterFrom< ? Actor< ActorLogic< TEvent, - State, - State, + State, + State, PersistedMachineState< - State + State >, ActorSystem, TInput diff --git a/packages/core/test/types.test.ts b/packages/core/test/types.test.ts index 6b328ddaeff..d87575c21b8 100644 --- a/packages/core/test/types.test.ts +++ b/packages/core/test/types.test.ts @@ -2972,4 +2972,19 @@ describe('tags', () => { } }); }); + + it('typed tags should work in state', () => { + const machine = createMachine({ + types: {} as { + tags: 'a' | 'b' | 'c'; + } + }); + + const actor = createActor(machine).start(); + + actor.getSnapshot().hasTag('a'); + actor.getSnapshot().hasTag('b'); + // @ts-expect-error + actor.getSnapshot().hasTag('unknown'); + }); }); diff --git a/packages/xstate-solid/src/types.ts b/packages/xstate-solid/src/types.ts index ffd3057be7b..ab9188e5b1a 100644 --- a/packages/xstate-solid/src/types.ts +++ b/packages/xstate-solid/src/types.ts @@ -15,10 +15,11 @@ type StateObject< TContext extends MachineContext, TEvent extends EventObject = EventObject, TActor extends ProvidedActor = ProvidedActor, + TTag extends string = string, TOutput = unknown, TResolvedTypesMeta = TypegenDisabled > = Pick< - State, + State, keyof AnyState >; @@ -28,10 +29,11 @@ export type CheckSnapshot = Snapshot extends State< infer TContext, infer TEvents, infer TActor, + infer TTag, infer TOutput, infer TResolvedTypesMeta > - ? StateObject + ? StateObject : Snapshot; type InternalMachineOpts< diff --git a/packages/xstate-test/src/types.ts b/packages/xstate-test/src/types.ts index 08d1b2d2089..c0af4d4870c 100644 --- a/packages/xstate-test/src/types.ts +++ b/packages/xstate-test/src/types.ts @@ -96,9 +96,11 @@ export type TestMachineOptions< export interface TestMeta { test?: ( testContext: T, - state: State + state: State ) => Promise | void; - description?: string | ((state: State) => string); + description?: + | string + | ((state: State) => string); skip?: boolean; } interface TestStateResult { @@ -168,7 +170,7 @@ export interface TestTransitionConfig< TTestContext > extends TransitionConfig { test?: ( - state: State, + state: State, testContext: TTestContext ) => void; }