Skip to content

Instantly share code, notes, and snippets.

@robfe
Last active June 26, 2017 19:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robfe/98a2f9a74a5a1a3d788fcdfa995f9b3c to your computer and use it in GitHub Desktop.
Save robfe/98a2f9a74a5a1a3d788fcdfa995f9b3c to your computer and use it in GitHub Desktop.
type StateMap<TStates extends string, TActions extends string> = {
[S in TStates]: { //for every string option in TStates:
[A in TActions]?: TStates; //optionally map to the next state if it's a valid action there
}
}; //AKA: Record<TStates, Partial<Record<TActions, TStates>>>;
class StateMachine<TStates extends string, TActions extends string>{
constructor(private _currentState: TStates, private stateMap: StateMap<TStates, TActions>) { }
get currentState() {
return this._currentState;
}
advance(action: TActions) {
let f = this.stateMap[this._currentState][action];
let next = f || this._currentState;
console.info(`${action}(${this._currentState}) => ${next}`);
this._currentState = next;
}
}
//client code
type TurnstileStates = "unlocked" | "locked" | "alarm";
type TurnstileActions = "coin" | "push" | "reset";
let s = new StateMachine<TurnstileStates, TurnstileActions>("unlocked",
{ // the point of all those generics:these action and state names are type checked and code-completed
unlocked: {
push: "locked"
},
locked: {
coin: "unlocked",
push: "alarm"
},
alarm: {
reset: "locked"
}
});
s.advance("push");
s.advance("coin");
s.advance("push");
s.advance("push");
s.advance("push");
s.advance("coin");
s.advance("reset");
s.advance("reset");
s.advance("coin");
@robfe
Copy link
Author

robfe commented Jun 26, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment