Skip to content

Instantly share code, notes, and snippets.

@ksaldana1
Created April 16, 2019 04:18
Show Gist options
  • Save ksaldana1/e38d68b6cab352ee395748fb879fe073 to your computer and use it in GitHub Desktop.
Save ksaldana1/e38d68b6cab352ee395748fb879fe073 to your computer and use it in GitHub Desktop.
import { EventObject } from 'xstate';
interface StateSchema<TEvents extends EventObject, TAllStates extends string> {
states?: { [K in TAllStates]: StateNode<TEvents, TAllStates> };
}
interface StateNode<
TEvents extends EventObject,
TAllStates extends string,
TContext = any
> extends StateSchema<TEvents, TAllStates> {
context: TContext;
transitions: Array<Transition<TEvents, TAllStates>>;
}
interface Transition<TEvents extends EventObject, TAllStates extends string> {
to: TAllStates;
event: TEvents;
}
// I'm sorry Anders... this isn't your fault
type ContextMapFromStateSchema<
TStateSchema extends StateSchema<any, any>
> = TStateSchema['states'] extends Record<infer Keys, any>
? { [K in Keys]: TStateSchema['states'][K]['context'] }
: never;
type ContextUnionFromStateSchema<
TStateSchema extends StateSchema<any, any>
> = TStateSchema['states'] extends Record<any, infer TStateNode>
? TStateNode extends { context: infer TCtx }
? TCtx
: never
: never;
type TransitionConfig<T extends StateSchema<any, any>> = {
[K in keyof T['states']]: {
// oh god oh no
on: {
[E in T['states'][K]['transitions'][number]['event']['type']]: ActionFunction<
ContextMapFromStateSchema<T>[K],
Extract<T['states'][K]['transitions'][number]['event'], { type: E }>,
ContextMapFromStateSchema<T>[Extract<
T['states'][K]['transitions'][number],
{ event: { type: E } }
>['to']]
>
};
}
};
type ActionFunction<TContext, TEvent extends EventObject, TReturnContext> = (
ctx: TContext,
event: TEvent
) => TReturnContext;
export enum States {
EVEN = 'EVEN',
ODD = 'ODD',
}
export enum EventTypes {
CLICK = 'CLICK',
DOUBLE_CLICK = 'DOUBLE_CLICK',
CLEAR = 'CLEAR',
}
interface ClickEvent {
type: EventTypes.CLICK;
}
interface DoubleClickEvent {
type: EventTypes.DOUBLE_CLICK;
}
interface ExampleSchema {
states: {
[States.EVEN]: {
context: {
value: 'EVEN';
};
transitions: [
{ to: States.ODD; event: ClickEvent },
{ to: States.EVEN; event: DoubleClickEvent }
];
};
[States.ODD]: {
context: {
value: 'ODD';
};
transitions: [
{ to: States.EVEN; event: ClickEvent },
{ to: States.ODD; event: DoubleClickEvent }
];
};
};
}
export const config: TransitionConfig<ExampleSchema> = {
[States.ODD]: {
on: {
CLICK: (_ctx /* ctx is { value: 'ODD' } */, _event) => {
return { value: 'EVE' };
},
DOUBLE_CLICK: (_ctx, _event) => {
return { value: 'ODD' };
},
},
},
[States.EVEN]: {
on: {
CLICK: (_ctx /* ctx is { value: 'EVEN' }, */, _event) => {
return { value: 'ODD' };
},
},
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment