Type Guards
Type Safety Everywhere
Cool autocomplete and type safety scenarios:
import { PromiseMachine, PromiseMachineEvent, createPromiseMachine } from 'matchina'
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
const slowAddingMachine = createPromiseMachine(async (a: number, b: number, t: number = 1000) => {
await delay(t)
return a + b
})
slowAddingMachine.send('execute', 1, 2)
const lastChange = slowAddingMachine.getChange()
if (lastChange.to.key === 'Resolved') {
const sum = lastChange.to.data
}
lastChange.to.match({
Pending: ([a, b, t]) => {
console.log(a, b, a + b)
},
Rejected: (err) => {
console.log(err.name, err.message)
}
}, false)
lastChange.from.as('Rejected').key === 'Rejected'
if (lastChange.to.is('Pending')) {
const [a, b, t] = lastChange.to.data
}
lastChange.match({
execute: (a, b, t=1000) => {
console.log(a, b, t)
},
reject: (err) => {
console.log(err.name, err.message)
}
}, false)
On states
- Factory autocompletes
states
types - State Creator autocompletes parameters
- State autocompletes
key
- State autocompletes
data
match
autocompletes state typesmatch
handler autocompletes state properties
On state machines
- transition config autocompletes state keys
- transition config autocompletes transition type and from properties
- autocomplete available states
- api autocompletes state types
- api autocompletes transition params
On state machine events
event
autocompletes eventtype
,to
,from
, andparams
event.match
autocompletes event typeevent.match
handler autocompletes event params- type assert matching
to.key
,from.key
and eventtype
On hooks and typeguards
Code Snippets
114 lines,
2385 chars
type type MyStateData = {
Idle: undefined;
Foo: (foo: string) => {
foo: string;
};
Bar: (bar: number) => {
bar: number;
};
}MyStateData = {
type Idle: undefinedIdle: undefined;
type Foo: (foo: string) => {
foo: string;
}Foo: (foo: stringfoo: string) => {
foo: stringfoo: string;
};
type Bar: (bar: number) => {
bar: number;
}Bar: (bar: numberbar: number) => {
bar: numberbar: number;
};
}
type type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates = type States<Specs extends SpecRecord> = { [T in string & keyof Specs]: CreateState<Specs, T>; }States<type MyStateData = {
Idle: undefined;
Foo: (foo: string) => {
foo: string;
};
Bar: (bar: number) => {
bar: number;
};
}MyStateData>
const const states: MyStatesstates: type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates = defineStates<MyStateData>(config: MyStateData): States<MyStateData>defineStates<type MyStateData = {
Idle: undefined;
Foo: (foo: string) => {
foo: string;
};
Bar: (bar: number) => {
bar: number;
};
}MyStateData>( {
type Idle: undefinedIdle: var undefinedundefined,
type Foo: (foo: string) => {
foo: string;
}Foo: (foo: stringfoo: string) => ({ foo: stringfoo }),
type Bar: (bar: number) => {
bar: number;
}Bar: (bar: numberbar: number) => ({ bar: numberbar })
})
type type Transitions = object & {
Idle?: {
[x: string]: FactoryMachineTransition<MyStates, "Idle", string> | undefined;
} | undefined;
Foo?: {
[x: string]: FactoryMachineTransition<MyStates, "Foo", string> | undefined;
} | undefined;
Bar?: {
...;
} | undefined;
} & {
...;
}Transitions =
type FactoryMachineTransitions<SF extends AnyStatesFactory> = object & { [FromStateKey in string & keyof SF]?: {
[x: string]: FactoryMachineTransition<SF, FromStateKey, string> | undefined;
} | undefined; }FactoryMachineTransitions<type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates> &
{
readonly type Idle: {
readonly start: "Foo";
}Idle: {
readonly start: "Foo"start: "Foo";
};
readonly type Foo: {
readonly toggle: "Bar";
readonly foo: (foo?: string) => MyStates["Foo"];
}Foo: {
readonly toggle: "Bar"toggle: "Bar";
readonly foo: (foo?: string) => MyStates["Foo"]foo: (foo: string | undefinedfoo?: string) => type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates['Foo']
};
readonly type Bar: {
readonly toggle: "Foo";
readonly increment: (bar: number) => (ev: any) => MyStates["Bar"];
}Bar: {
readonly toggle: "Foo"toggle: "Foo";
readonly increment: (bar: number) => (ev: any) => MyStates["Bar"]increment: (bar: numberbar: number) => (ev: anyev: any) => type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates['Bar'];
}
}
const const transitions: Transitionstransitions: type Transitions = object & {
Idle?: {
[x: string]: FactoryMachineTransition<MyStates, "Idle", string> | undefined;
} | undefined;
Foo?: {
[x: string]: FactoryMachineTransition<MyStates, "Foo", string> | undefined;
} | undefined;
Bar?: {
...;
} | undefined;
} & {
...;
}Transitions = {
type Idle: {
[x: string]: FactoryMachineTransition<MyStates, "Idle", string> | undefined;
} & {
readonly start: "Foo";
}Idle: {
start: "Foo"start: 'Foo'
},
type Foo: {
[x: string]: FactoryMachineTransition<MyStates, "Foo", string> | undefined;
} & {
readonly toggle: "Bar";
readonly foo: (foo?: string) => MyStates["Foo"];
}Foo: {
toggle: "Bar"toggle: 'Bar',
foo: (foo?: string) => MyStates["Foo"]foo: (foo: string | undefinedfoo = 'foo') => const states: MyStatesstates.type Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>Foo(`my-${foo: stringfoo}`) as any,
},
type Bar: {
[x: string]: FactoryMachineTransition<MyStates, "Bar", string> | undefined;
} & {
readonly toggle: "Foo";
readonly increment: (bar: number) => (ev: any) => MyStates["Bar"];
}Bar: {
toggle: "Foo"toggle: 'Foo',
increment: (bar: number) => (ev: any) => MyStates["Bar"]increment: (bar: numberbar: number) => (ev: anyev: any) =>
const states: MyStatesstates.type Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>Barev: any(ev.anyfrom.anydata.anybar + bar: numberbar) as any
}
}
const const machine: FactoryMachine<{
states: MyStates;
transitions: Transitions;
}>machine = createFactoryMachine<MyStates, Transitions, {
states: MyStates;
transitions: Transitions;
}, FactoryMachineEvent<{
states: MyStates;
transitions: Transitions;
}>>(states: MyStates, transitions: Transitions, init: "Idle" | ... 2 more ... | StateMatchbox<...>): FactoryMachine<{
...;
}>createFactoryMachine<type MyStates = {
Idle: () => StateMatchbox<"Idle", MyStateData>;
Foo: (foo: string) => StateMatchbox<"Foo", MyStateData>;
Bar: (bar: number) => StateMatchbox<"Bar", MyStateData>;
}MyStates, type Transitions = object & {
Idle?: {
[x: string]: FactoryMachineTransition<MyStates, "Idle", string> | undefined;
} | undefined;
Foo?: {
[x: string]: FactoryMachineTransition<MyStates, "Foo", string> | undefined;
} | undefined;
Bar?: {
...;
} | undefined;
} & {
...;
}Transitions>(
const states: MyStatesstates,
const transitions: Transitionstransitions,
'Idle'
)
const machine: FactoryMachine<{
states: MyStates;
transitions: Transitions;
}>machine.FactoryMachine<{ states: MyStates; transitions: Transitions; }>.states: MyStatesstates.B- Bar
ar(1).data: {
bar: number;
}data.bar: numberbar === 1
// const change = machine.getChange()
// const { type, from: {key: fromKey}, to: { key: toKey } } = change
const const slowAddingMachine: PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>slowAddingMachine = createPromiseMachine<(a: number, b: number, t?: number) => Promise<number>>(makePromise?: ((a: number, b: number, t?: number) => Promise<number>) | undefined): PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>createPromiseMachine(async (a: numbera: number, b: numberb: number, t: numbert: number = 1000) => {
await function delay(ms: number): Promise<unknown>delay(t: numbert)
return a: numbera + b: numberb
})
const slowAddingMachine: PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>slowAddingMachine.StateMachine<FactoryMachineEvent<{ states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>; transitions: PromiseTransitions; }>>.send: (type: "execute" | "resolve" | "reject", ...params: (any[] & [a: number, b: number, t?: number | undefined]) | (any[] & [data: number]) | (any[] & [error: Error])) => voidsend('execute', 1, 2)
const const api: {
readonly execute: (...args: any[] & [a: number, b: number, t?: number | undefined]) => void;
readonly resolve: (...args: any[] & [data: number]) => void;
readonly reject: (...args: any[] & [error: Error]) => void;
} & objectapi = createApi<PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>, keyof PromiseTransitions>(machine: PromiseMachine<(a: number, b: number, t?: number) => Promise<...>>, filterStateKey?: keyof PromiseTransitions | undefined): FactoryMachineApi<PromiseMachine<...>>createApiconst slowAddingMachine: PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>(slowAddingMachine)
const api: {
readonly execute: (...args: any[] & [a: number, b: number, t?: number | undefined]) => void;
readonly resolve: (...args: any[] & [data: number]) => void;
readonly reject: (...args: any[] & [error: Error]) => void;
} & objectapi.r- reject
- resolve
eject(new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)Error('test'))
const const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange = const slowAddingMachine: PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>slowAddingMachine.StateMachine<FactoryMachineEvent<{ states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>; transitions: PromiseTransitions; }>>.getChange(): FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>getChange()
const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange.to: ({
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">) | ({
...;
} & MemberExtensionsFromDataSpecs<...>) | ({
...;
} & MemberExtensionsFromDataSpecs<...>)to.MemberExtensionsFromDataSpecs<DataSpecs, TagProp extends string>.match: MatchMemberData
<void, false>(cases: Partial<Cases<MemberData<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>>, void> & {
...;
}>, exhaustive?: false | undefined) => voidmatch({
Pending?: ((value: [a: number, b: number, t?: number | undefined]) => void) | undefinedPending: ([a: numbera, b: numberb, t: number | undefinedt]) => {
},
Rejected?: ((value: Error) => void) | undefinedRejected: (err: Errorerr) => {
var console: Consoleconsole.Console.log(...data: any[]): void[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(err: Errorerr.Error.name: stringname, err: Errorerr.Error.message: stringmessage)
}
}, false)
const { const from: ({
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">) | ({
...;
} & MemberExtensionsFromDataSpecs<...>)from } = const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange
if (matchChange<FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>, "execute" | ... 2 more ... | undefined, "Idle" | ... 1 more ... | undefined, "Rejected">(changeEvent: FactoryMachineEvent<...>, ...rest: [type?: ...] | [filter: ...]): changeEvent is StateMachineEvent<...> & ... 2 more ... & {
...;
}matchChange(
const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange,
{
to?: "Rejected" | undefinedto: 'Rejected'
})) {
const lastChange: FactoryMachineTransitionEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}, "Pending", "reject", "Rejected">lastChange.type: "reject"type = 'reject'
}
const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange.match: MatchInvocation
<void, false>(cases: Partial<InvocationCases<FlatMemberUnion<StateEventTransitionSenders<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>>, void> & {
...;
}>, exhaustive?: false | undefined) => voidmatch({
execute?: ((...params: any[] & [a: number, b: number, t?: number | undefined]) => void) | undefinedexecute: (a: numbera, b: numberb, t: number | undefinedt=1000) => {
var console: Consoleconsole.Console.log(...data: any[]): void[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(a: numbera, b: numberb, t: numbert)
},
reject?: ((...params: any[] & [error: Error]) => void) | undefinedreject: (err: Errorerr) => {
var console: Consoleconsole.Console.log(...data: any[]): void[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(err: Errorerr.Error.name: stringname, err: Errorerr.Error.message: stringmessage)
}
}, false)
if const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>(lastChange.to: ({
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">) | ({
...;
} & MemberExtensionsFromDataSpecs<...>) | ({
...;
} & MemberExtensionsFromDataSpecs<...>)to.MemberExtensionsFromDataSpecs<DataSpecs, TagProp extends string>.is: <"Pending">(key: "Pending") => this is MemberFromDataSpecs<T, DataSpecs, TagProp>is('Pending')) {
const [const a: numbera, const b: numberb, const t: number | undefinedt] = const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange.to: {
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">to.data: [a: number, b: number, t?: number | undefined]data
}
const slowAddingMachine: PromiseMachine<(a: number, b: number, t?: number) => Promise<number>>slowAddingMachine.FactoryMachine<{ states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>; transitions: PromiseTransitions; }>.states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>states.type Idle: () => StateMatchbox<"Idle", PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>>Idle().key: "Idle"key
const { const type: "execute" | "resolve" | "reject"type, from: ({
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">) | ({
...;
} & MemberExtensionsFromDataSpecs<...>)from: {key: "Idle" | "Pending"key: const fromKey: "Idle" | "Pending"fromKey}, to: ({
key: "Pending";
data: [a: number, b: number, t?: number | undefined];
} & MemberExtensionsFromDataSpecs<PromiseStateCreators<(a: number, b: number, t?: number) => Promise<number>, Error>, "key">) | ({
...;
} & MemberExtensionsFromDataSpecs<...>) | ({
...;
} & MemberExtensionsFromDataSpecs<...>)to: { key: "Pending" | "Rejected" | "Resolved"key: const toKey: "Pending" | "Rejected" | "Resolved"toKey } } = const lastChange: FactoryMachineEvent<{
states: PromiseStates<(a: number, b: number, t?: number) => Promise<number>, Error>;
transitions: PromiseTransitions;
}>lastChange