Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v5] Remove Typestate #2876

Merged
merged 4 commits into from
Dec 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/small-papayas-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': major
---

Typings for `Typestate` have been removed. The reason for this is that types for typestates needed to be manually specified, which is unsound because it is possible to specify _impossible_ typestates; i.e., typings for a state's `value` and `context` that are impossible to achieve.
2 changes: 1 addition & 1 deletion packages/core/actions/ExecutableAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class ExecutableAction<
this.type = actionObject.type;
this.params = actionObject.params ?? {};
}
public execute(state: State<TContext, TEvent, any>) {
public execute(state: State<TContext, TEvent>) {
const context = this.context ?? state.context;

return this._exec?.(context, state.event, {
Expand Down
16 changes: 5 additions & 11 deletions packages/core/src/Machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,23 @@ import {
MachineConfig,
EventObject,
AnyEventObject,
Typestate,
MachineContext
} from './types';
import { StateMachine } from './StateMachine';

export function createMachine<
TContext extends MachineContext,
TEvent extends EventObject = AnyEventObject,
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
TEvent extends EventObject = AnyEventObject
>(
config: MachineConfig<TContext, TEvent>,
options?: Partial<MachineImplementations<TContext, TEvent>>
): StateMachine<TContext, TEvent, TTypestate>;
): StateMachine<TContext, TEvent>;
export function createMachine<
TContext extends MachineContext,
TEvent extends EventObject = AnyEventObject,
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
TEvent extends EventObject = AnyEventObject
>(
definition: MachineConfig<TContext, TEvent>,
implementations?: Partial<MachineImplementations<TContext, TEvent>>
): StateMachine<TContext, TEvent, TTypestate> {
return new StateMachine<TContext, TEvent, TTypestate>(
definition,
implementations
);
): StateMachine<TContext, TEvent> {
return new StateMachine<TContext, TEvent>(definition, implementations);
}
35 changes: 11 additions & 24 deletions packages/core/src/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type {
StateConfig,
SCXML,
TransitionDefinition,
Typestate,
HistoryValue,
ActorRef,
MachineContext,
Expand All @@ -26,9 +25,8 @@ import type { StateMachine } from './StateMachine';

export function isState<
TContext extends MachineContext,
TEvent extends EventObject,
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
>(state: object | string): state is State<TContext, TEvent, TTypestate> {
TEvent extends EventObject
>(state: object | string): state is State<TContext, TEvent> {
if (isString(state)) {
return false;
}
Expand All @@ -38,12 +36,11 @@ export function isState<

export class State<
TContext extends MachineContext,
TEvent extends EventObject = EventObject,
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
TEvent extends EventObject = EventObject
> {
public value: StateValue;
public context: TContext;
public history?: State<TContext, TEvent, TTypestate>;
public history?: State<TContext, TEvent>;
public historyValue: HistoryValue<TContext, TEvent> = {};
public actions: BaseActionObject[] = [];
public meta: any = {};
Expand Down Expand Up @@ -79,7 +76,7 @@ export class State<
*/
public children: Record<string, ActorRef<any>>;
public tags: Set<string>;
public machine: StateMachine<TContext, TEvent, TTypestate> | undefined;
public machine: StateMachine<TContext, TEvent> | undefined;
/**
* Creates a new State instance for the given `stateValue` and `context`.
* @param stateValue
Expand All @@ -89,9 +86,9 @@ export class State<
TContext extends MachineContext,
TEvent extends EventObject = EventObject
>(
stateValue: State<TContext, TEvent, any> | StateValue,
stateValue: State<TContext, TEvent> | StateValue,
context: TContext = {} as TContext
): State<TContext, TEvent, any> {
): State<TContext, TEvent> {
if (stateValue instanceof State) {
if (stateValue.context !== context) {
return new State<TContext, TEvent>({
Expand Down Expand Up @@ -133,17 +130,15 @@ export class State<
public static create<
TContext extends MachineContext,
TEvent extends EventObject = EventObject
>(config: StateConfig<TContext, TEvent>): State<TContext, TEvent, any> {
>(config: StateConfig<TContext, TEvent>): State<TContext, TEvent> {
return new State(config);
}
/**
* Creates a new `State` instance for the given `stateValue` and `context` with no actions (side-effects).
* @param stateValue
* @param context
*/
public static inert<TState extends State<any, any, any>>(
state: TState
): TState;
public static inert<TState extends State<any, any>>(state: TState): TState;
public static inert<
TContext extends MachineContext,
TEvent extends EventObject = EventObject
Expand Down Expand Up @@ -241,17 +236,9 @@ export class State<
* Whether the current state value is a subset of the given parent state value.
* @param parentStateValue
*/
public matches<TSV extends TTypestate['value']>(
public matches<TSV extends StateValue>(
parentStateValue: TSV
): this is State<
(TTypestate extends any
? { value: TSV; context: any } extends TTypestate
? TTypestate
: never
: never)['context'],
TEvent,
TTypestate
> & { value: TSV } {
): this is State<TContext, TEvent> & { value: TSV } {
return matchesState(parentStateValue as StateValue, this.value);
}

Expand Down
30 changes: 12 additions & 18 deletions packages/core/src/StateMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type {
EventObject,
MachineConfig,
SCXML,
Typestate,
Transitions,
MachineSchema,
StateNodeDefinition,
Expand Down Expand Up @@ -59,8 +58,7 @@ function resolveContext<TContext>(

export class StateMachine<
TContext extends MachineContext = any,
TEvent extends EventObject = EventObject,
TTypestate extends Typestate<TContext> = any
TEvent extends EventObject = EventObject
> {
private _context: () => TContext;
public get context(): TContext {
Expand Down Expand Up @@ -172,7 +170,7 @@ export class StateMachine<
*
* @param state The state to resolve
*/
public resolveState(state: State<TContext, TEvent, any>): typeof state {
public resolveState(state: State<TContext, TEvent>): typeof state {
const configuration = Array.from(
getConfiguration(getStateNodes(this.root, state.value))
);
Expand All @@ -191,9 +189,9 @@ export class StateMachine<
* @param event The received event
*/
public transition(
state: StateValue | State<TContext, TEvent, TTypestate> = this.initialState,
state: StateValue | State<TContext, TEvent> = this.initialState,
event: Event<TEvent> | SCXML.Event<TEvent>
): State<TContext, TEvent, TTypestate> {
): State<TContext, TEvent> {
const currentState = toState(state, this);

return macrostep(currentState, event, this);
Expand All @@ -207,9 +205,9 @@ export class StateMachine<
* @param event The received event
*/
public microstep(
state: StateValue | State<TContext, TEvent, TTypestate> = this.initialState,
state: StateValue | State<TContext, TEvent> = this.initialState,
event: Event<TEvent> | SCXML.Event<TEvent>
): State<TContext, TEvent, TTypestate> {
): State<TContext, TEvent> {
const resolvedState = toState(state, this);
const _event = toSCXMLEvent(event);

Expand Down Expand Up @@ -240,7 +238,7 @@ export class StateMachine<
return transitionNode(this.root, state.value, state, _event) || [];
}

public get first(): State<TContext, TEvent, TTypestate> {
public get first(): State<TContext, TEvent> {
const pseudoinitial = this.resolveState(
State.from(
getStateValue(this.root, getConfiguration([this.root])),
Expand All @@ -256,21 +254,17 @@ export class StateMachine<
* The initial State instance, which includes all actions to be executed from
* entering the initial state.
*/
public get initialState(): State<TContext, TEvent, TTypestate> {
public get initialState(): State<TContext, TEvent> {
const nextState = resolveMicroTransition(this, [], this.first, undefined);
return macrostep(nextState, null as any, this);
}

/**
* Returns the initial `State` instance, with reference to `self` as an `ActorRef`.
*/
public getInitialState(): State<TContext, TEvent, TTypestate> {
public getInitialState(): State<TContext, TEvent> {
const nextState = resolveMicroTransition(this, [], this.first, undefined);
return macrostep(nextState, null as any, this) as State<
TContext,
TEvent,
TTypestate
>;
return macrostep(nextState, null as any, this) as State<TContext, TEvent>;
}

public getStateNodeById(stateId: string): StateNode<TContext, TEvent> {
Expand All @@ -297,11 +291,11 @@ export class StateMachine<

public createState(
stateConfig: State<TContext, TEvent> | StateConfig<TContext, TEvent>
): State<TContext, TEvent, TTypestate> {
): State<TContext, TEvent> {
const state =
stateConfig instanceof State
? stateConfig
: (new State(stateConfig) as State<TContext, TEvent, TTypestate>);
: (new State(stateConfig) as State<TContext, TEvent>);
state.machine = this;
return state;
}
Expand Down
7 changes: 2 additions & 5 deletions packages/core/src/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,7 @@ export function spawnObservable<T extends EventObject>(
return spawn(createObservableBehavior(lazyObservable), name);
}

export function spawnMachine(
machine: StateMachine<any, any, any>,
name?: string
) {
export function spawnMachine(machine: StateMachine<any, any>, name?: string) {
return spawn(createMachineBehavior(machine), name);
}

Expand All @@ -130,7 +127,7 @@ export function spawnFrom<
TEvent extends EventObject,
TEmitted extends State<any, any>
>(
entity: StateMachine<TEmitted['context'], any, TEmitted['event']>,
entity: StateMachine<TEmitted['context'], TEmitted['event']>,
name?: string
): ObservableActorRef<TEvent, TEmitted>;
export function spawnFrom<TEvent extends EventObject>(
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/behaviors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ export function createBehaviorFrom<
TEvent extends EventObject,
TEmitted extends State<any, any>
>(
entity: StateMachine<TEmitted['context'], any, TEmitted['event']>
entity: StateMachine<TEmitted['context'], TEmitted['event']>
): Behavior<TEvent, TEmitted>;
export function createBehaviorFrom<TEvent extends EventObject>(
entity: InvokeCallback
Expand Down
Loading