Skip to content

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 types
  • match 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 event type, to, from, and params
  • event.match autocompletes event type
  • event.match handler autocompletes event params
  • type assert matching to.key, from.key and event type

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