undoable TS typing (#2374)

* type undoable-related TS

* style change to type declaration
This commit is contained in:
Bruce Martin
2021-08-12 17:14:09 -07:00
committed by GitHub
parent 925b785b1f
commit 4b417cb5a5
5 changed files with 266 additions and 195 deletions
+56 -41
View File
@@ -13,18 +13,17 @@ Where:
to: state_name_transitioning_to,
from: state_name_transitioning_from,
event: value_that_will_cause_transition,
action: optional_callback_upon_transition
action: callback_upon_transition
}
The transition will be provided to the action callback, so other data
may be stored in the transition object for use by the action callback.
* onErrorCallback - a callback function called if the FSM receives an event
for which it has no defined transition.
Interface:
* states - property containing the state names. A Set(), contianing the
* states - property containing the state names. A Set(), containing the
union of to: and from: values.
* events - property containing all of the accepted event values. Set().
* events - property containing all of the accepted event values. Set().
* graph - a Map of Maps, organized as graph[eventValue][fromStateValue]
* clone() - clone the entire statemachine.
* next(eventValue) - drive the FSM to the next state. If the event
@@ -40,47 +39,69 @@ Example:
const fsm = new StateMachine("A", transitions, () => { throw new Error("oops") });
fsm.next("yo"); // returns 42
*/
export default class StateMachine {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
events: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
graph: any;
export type FsmState = number | string;
export type FsmEvent = string; // by convention, we assume Events are redux action types, aka strings
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
onError: any;
export type FsmActionFn<ActionReturnType> = (
fsm: StateMachine<ActionReturnType>,
transition: FsmTransition<ActionReturnType>,
data: unknown
) => ActionReturnType;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
state: any;
export interface FsmTransition<ActionReturnType> {
from: FsmState;
to: FsmState;
event: FsmEvent;
action: FsmActionFn<ActionReturnType>;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
states: any;
export type FsmErrorFn<ActionReturnType> = (
fsm: StateMachine<ActionReturnType>,
event: FsmEvent,
state: FsmState
) => ActionReturnType;
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
constructor(initState: any, transitions: any, onError: any) {
this.onError = onError || (() => undefined);
export class StateMachine<ActionReturnType> {
events: Set<FsmEvent>;
graph: Map<FsmEvent, Map<FsmState, FsmTransition<ActionReturnType>>>;
onError: FsmErrorFn<ActionReturnType>;
state: FsmState;
states: Set<FsmState>;
constructor(
initState: FsmState,
transitions: FsmTransition<ActionReturnType>[],
onError: FsmErrorFn<ActionReturnType>
) {
this.onError = onError;
this.state = initState;
// all states
this.states = new Set(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
transitions.reduce((names: any, tsn: any) => {
names.push(tsn.from);
names.push(tsn.to);
return names;
}, [])
transitions.reduce(
(names: Array<FsmState>, tsn: FsmTransition<ActionReturnType>) => {
names.push(tsn.from);
names.push(tsn.to);
return names;
},
[]
)
);
// all transition names (aka events)
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.events = new Set(transitions.map((tsn: any) => tsn.event));
this.events = new Set(
transitions.map((tsn: FsmTransition<ActionReturnType>) => tsn.event)
);
// the transition graph.
// graph[event][from] -> transition
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.graph = transitions.reduce((graph: any, tsn: any) => {
this.graph = transitions.reduce((graph, tsn) => {
const { event, from } = tsn;
if (!graph.has(event)) graph.set(event, new Map());
const tsnMap = graph.get(event);
@@ -89,29 +110,23 @@ export default class StateMachine {
}, new Map());
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
clone(initState: any) {
// @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 2.
const fsm = new StateMachine(initState, []);
fsm.onError = this.onError;
clone(initState: FsmState): StateMachine<ActionReturnType> {
const fsm = new StateMachine<ActionReturnType>(initState, [], this.onError);
fsm.states = this.states;
fsm.events = this.events;
fsm.graph = this.graph;
return fsm;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
next(event: any, data: any) {
next(event: FsmEvent, data: unknown): ActionReturnType {
const { graph, state } = this;
const tsnMap = graph.get(event);
if (!tsnMap) return this.onError(this, event, state, undefined);
if (!tsnMap) return this.onError(this, event, state);
const transition = tsnMap.get(state);
if (!transition) return this.onError(this, event, state, undefined);
if (!transition) return this.onError(this, event, state);
this.state = transition.to;
return transition.action
? transition.action(this, transition, data)
: undefined;
return transition.action(this, transition, data);
}
}