add Context generic type

main
Brian Sakal 2 years ago
parent e37161d3a0
commit 0495f1d87a

@ -1,43 +1,43 @@
type EventShape = [name:string, payload:any]; type Event_T = [name:string, payload:any];
export interface Machine_T<S,E extends EventShape> { export interface Machine_T<S,E extends Event_T,C> {
states: Array<State_T<S,E>> states: Array<State_T<S,E,C>>
} }
export interface State_T<S,E extends EventShape> { export interface State_T<S,E extends Event_T,C> {
name: S; name: S;
ons: Array<On_T<S,E>>; ons: Array<On_T<S,E,C>>;
} }
export interface On_T<S,E extends EventShape> { export interface On_T<S,E extends Event_T,C> {
eventName: E[0]; eventName: E[0];
reactions: Array<Do_T | Goto_T<S>>; reactions: Array<Do_T<E,C> | Goto_T<S>>;
}; };
export interface Do_T { export interface Do_T<E extends Event_T, C> {
type: 'Do'; type: 'Do';
fn: DoFn_T; fn: DoFn_T<E,C>;
}; };
export type DoFn_T = (ctx,e,self)=>any; export type DoFn_T<E extends Event_T, C> = (ctx:C,e:E,self)=>any;
export interface Goto_T<S> { export interface Goto_T<S> {
type: 'Goto'; type: 'Goto';
targetStateName: S; targetStateName: S;
}; };
export const Machine = function<S,E extends EventShape>(...states:Array<State_T<S,E>>) : Machine_T<S,E> { return {states}; }; export const Machine = function<S,E extends Event_T,C>(...states:Array<State_T<S,E,C>>) : Machine_T<S,E,C> { return {states}; };
export const State = function<S,E extends EventShape>(name:S, ...ons:Array<On_T<S,E>>) : State_T<S,E>{ return {name, ons}; }; export const State = function<S,E extends Event_T,C>(name:S, ...ons:Array<On_T<S,E,C>>) : State_T<S,E,C>{ return {name, ons}; };
export const On = function<S,E extends EventShape>(eventName:E[0], ...reactions:Array<Do_T | Goto_T<S>>) : On_T<S,E>{ return {eventName, reactions}; }; export const On = function<S,E extends Event_T,C>(eventName:E[0], ...reactions:Array<Do_T<E,C> | Goto_T<S>>) : On_T<S,E,C>{ return {eventName, reactions}; };
export const Do = function(fn:DoFn_T) : Do_T{ return {type:'Do', fn}; }; export const Do = function<E extends Event_T, C>(fn:DoFn_T<E,C>) : Do_T<E,C>{ return {type:'Do', fn}; };
export const Goto = function<S>(targetStateName:S) : Goto_T<S> { return {type:'Goto', targetStateName} }; export const Goto = function<S>(targetStateName:S) : Goto_T<S> { return {type:'Goto', targetStateName} };
interface Tick_T { interface Tick_T<E extends Event_T, C> {
doFunctions: Array<DoFn_T> doFunctions: Array<DoFn_T<E,C>>
}; };
const Tick = function(doFunctions : Array<DoFn_T>) : Tick_T{ return {doFunctions}; }; const Tick = function<E extends Event_T,C>(doFunctions : Array<DoFn_T<E,C>>) : Tick_T<E,C>{ return {doFunctions}; };
export interface Interpreter_T<S,E extends EventShape,C> { export interface Interpreter_T<S,E extends Event_T,C> {
machine: Machine_T<S,E>; machine: Machine_T<S,E,C>;
state: S; state: S;
context: C | {}; context: C | {};
} }
export function interpret<S,E extends EventShape,C>(machine:Machine_T<S,E>, options?:{state?:S, context?:C | {}}) : Interpreter_T<S,E,C>{ export function interpret<S,E extends Event_T,C>(machine:Machine_T<S,E,C>, options?:{state?:S, context?:C | {}}) : Interpreter_T<S,E,C>{
let {state, context} = options || {}; let {state, context} = options || {};
if(typeof state === 'undefined'){ state = machine.states[0].name; } if(typeof state === 'undefined'){ state = machine.states[0].name; }
if(typeof context === 'undefined'){ context = {}; } if(typeof context === 'undefined'){ context = {}; }
@ -46,12 +46,12 @@ export function interpret<S,E extends EventShape,C>(machine:Machine_T<S,E>, opti
/** Helper function for `send()` /** Helper function for `send()`
*/ */
function getState<S,E extends EventShape,C>(interpreter : Interpreter_T<S,E,C>) : State_T<S,E>{ function getState<S,E extends Event_T,C>(interpreter : Interpreter_T<S,E,C>) : State_T<S,E,C>{
return interpreter.machine.states.find((state)=>state.name===interpreter.state) as unknown as State_T<S,E>; return interpreter.machine.states.find((state)=>state.name===interpreter.state) as unknown as State_T<S,E,C>;
} }
/** Helper function for `send()` /** Helper function for `send()`
*/ */
function getOns<S,E extends EventShape>(state : State_T<S,E>, event:E) : Array<On_T<S,E>>{ function getOns<S,E extends Event_T,C>(state : State_T<S,E,C>, event:E) : Array<On_T<S,E,C>>{
return state.ons.filter((on)=>on.eventName===event[0]); return state.ons.filter((on)=>on.eventName===event[0]);
} }
/** Inject an Event into the Interpreter's "tick queue". /** Inject an Event into the Interpreter's "tick queue".
@ -64,7 +64,7 @@ function getOns<S,E extends EventShape>(state : State_T<S,E>, event:E) : Array<O
* whether to run a reaction at all. If an Event is received, and is specified to be applied on a past * whether to run a reaction at all. If an Event is received, and is specified to be applied on a past
* Tick, it is discarded. * Tick, it is discarded.
*/ */
export function send<S,E extends EventShape,C>(interpreter : Interpreter_T<S,E,C>, event:E){ export function send<S,E extends Event_T,C>(interpreter : Interpreter_T<S,E,C>, event:E){
const state = getState(interpreter); const state = getState(interpreter);
const ons = getOns(state, event); const ons = getOns(state, event);
const tick = Tick(ons const tick = Tick(ons
@ -75,10 +75,10 @@ export function send<S,E extends EventShape,C>(interpreter : Interpreter_T<S,E,C
return reaction.fn; return reaction.fn;
} }
else if(reaction.type === 'Goto'){ else if(reaction.type === 'Goto'){
return (ctx,e,self)=>{}; return (ctx:C,e:E,self)=>{};
} }
else{ else{
return (ctx,e,self)=>{}; return (ctx:C,e:E,self)=>{};
} }
}) })
); );

@ -1,12 +1,13 @@
import { Machine, State, On, Do, Goto, Spawn, Unspawn } from '../index'; import { Machine, State, On, Do, Goto, Spawn, Unspawn } from '../index';
const beginTimer = ()=>{}; const beginTimer = (ctx:C, e:E)=>{};
type S = 'green' | 'yellow' | 'red'; type S = 'green' | 'yellow' | 'red';
type E = ['entry',null] | ['timer-finished',null]; type E = ['entry',null] | ['timer-finished',null];
type C = null;
const machine = const machine =
Machine<S,E>( Machine<S,E,C>(
State('green', State('green',
On('entry', On('entry',
Do(beginTimer) Do(beginTimer)

Loading…
Cancel
Save