Skip to content

Commit

Permalink
Pass through Typestate type param correctly and default it everywhere…
Browse files Browse the repository at this point in the history
… to the same value
  • Loading branch information
Andarist committed Jul 17, 2020
1 parent 597cfdc commit 8662e54
Show file tree
Hide file tree
Showing 14 changed files with 73 additions and 52 deletions.
5 changes: 5 additions & 0 deletions .changeset/polite-goats-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@xstate/vue': minor
---

`useMachine` and `useService` cant be now parametrized with a `TTypestate` parameter which makes leveraging typestates possible on their returned values.
5 changes: 5 additions & 0 deletions .changeset/ten-phones-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': minor
---

All `TTypestate` type parameters default to `{ value: any; context: TContext }` now and the parametrized type is passed correctly between various types which results in more accurate types involving typestates.
2 changes: 1 addition & 1 deletion packages/core/src/Machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function Machine<
? (initialContext as () => TContext)()
: initialContext;

return new StateNode<TContext, TStateSchema, TEvent, any>(
return new StateNode<TContext, TStateSchema, TEvent>(
config,
options,
resolvedInitialContext
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function isState<
TContext,
TEvent extends EventObject,
TStateSchema extends StateSchema<TContext> = any,
TTypestate extends Typestate<TContext> = any
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
>(
state: object | string
): state is State<TContext, TEvent, TStateSchema, TTypestate> {
Expand Down Expand Up @@ -89,7 +89,7 @@ export class State<
public value: StateValue;
public context: TContext;
public historyValue?: HistoryValue | undefined;
public history?: State<TContext, TEvent, TStateSchema>;
public history?: State<TContext, TEvent, TStateSchema, TTypestate>;
public actions: Array<ActionObject<TContext, TEvent>> = [];
public activities: ActivityMap = EMPTY_ACTIVITY_MAP;
public meta: any = {};
Expand Down
14 changes: 8 additions & 6 deletions packages/core/src/StateNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class StateNode<
TContext = any,
TStateSchema extends StateSchema = any,
TEvent extends EventObject = EventObject,
TTypestate extends Typestate<TContext> = any
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
> {
/**
* The relative key of the state node, which represents its location in the overall state value.
Expand Down Expand Up @@ -614,8 +614,8 @@ class StateNode<
* @param state The state value or State instance
*/
public getStateNodes(
state: StateValue | State<TContext, TEvent>
): Array<StateNode<TContext, any, TEvent>> {
state: StateValue | State<TContext, TEvent, any, TTypestate>
): Array<StateNode<TContext, any, TEvent, TTypestate>> {
if (!state) {
return [];
}
Expand All @@ -636,7 +636,8 @@ class StateNode<
const subStateNodes: Array<StateNode<
TContext,
any,
TEvent
TEvent,
TTypestate
>> = subStateKeys.map((subStateKey) => this.getStateNode(subStateKey));

return subStateNodes.concat(
Expand All @@ -646,7 +647,7 @@ class StateNode<
);

return allSubStateNodes.concat(subStateNode);
}, [] as Array<StateNode<TContext, any, TEvent>>)
}, [] as Array<StateNode<TContext, any, TEvent, TTypestate>>)
);
}

Expand Down Expand Up @@ -1025,7 +1026,8 @@ class StateNode<
* @param context The current context (extended state) of the current state
*/
public transition(
state: StateValue | State<TContext, TEvent> = this.initialState,
state: StateValue | State<TContext, TEvent, any, TTypestate> = this
.initialState,
event: Event<TEvent> | SCXML.Event<TEvent>,
context?: TContext
): State<TContext, TEvent, TStateSchema, TTypestate> {
Expand Down
20 changes: 10 additions & 10 deletions packages/core/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export type StateListener<
TContext,
TEvent extends EventObject,
TStateSchema extends StateSchema<TContext> = any,
TTypestate extends Typestate<TContext> = any
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
> = (
state: State<TContext, TEvent, TStateSchema, TTypestate>,
event: TEvent
Expand Down Expand Up @@ -126,7 +126,7 @@ export class Interpreter<
TContext,
TStateSchema extends StateSchema = any,
TEvent extends EventObject = EventObject,
TTypestate extends Typestate<TContext> = any
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
> implements Actor<State<TContext, TEvent, TStateSchema, TTypestate>, TEvent> {
/**
* The default interpreter options:
Expand Down Expand Up @@ -392,7 +392,7 @@ export class Interpreter<
*/
public onEvent(
listener: EventListener
): Interpreter<TContext, TStateSchema, TEvent> {
): Interpreter<TContext, TStateSchema, TEvent, TTypestate> {
this.eventListeners.add(listener);
return this;
}
Expand All @@ -402,7 +402,7 @@ export class Interpreter<
*/
public onSend(
listener: EventListener
): Interpreter<TContext, TStateSchema, TEvent> {
): Interpreter<TContext, TStateSchema, TEvent, TTypestate> {
this.sendListeners.add(listener);
return this;
}
Expand All @@ -412,7 +412,7 @@ export class Interpreter<
*/
public onChange(
listener: ContextListener<TContext>
): Interpreter<TContext, TStateSchema, TEvent> {
): Interpreter<TContext, TStateSchema, TEvent, TTypestate> {
this.contextListeners.add(listener);
return this;
}
Expand All @@ -422,7 +422,7 @@ export class Interpreter<
*/
public onStop(
listener: Listener
): Interpreter<TContext, TStateSchema, TEvent> {
): Interpreter<TContext, TStateSchema, TEvent, TTypestate> {
this.stopListeners.add(listener);
return this;
}
Expand All @@ -432,7 +432,7 @@ export class Interpreter<
*/
public onDone(
listener: EventListener<DoneEvent>
): Interpreter<TContext, TStateSchema, TEvent> {
): Interpreter<TContext, TStateSchema, TEvent, TTypestate> {
this.doneListeners.add(listener);
return this;
}
Expand All @@ -442,7 +442,7 @@ export class Interpreter<
*/
public off(
listener: (...args: any[]) => void
): Interpreter<TContext, TStateSchema, TEvent> {
): Interpreter<TContext, TStateSchema, TEvent, TTypestate> {
this.listeners.delete(listener);
this.eventListeners.delete(listener);
this.sendListeners.delete(listener);
Expand Down Expand Up @@ -499,7 +499,7 @@ export class Interpreter<
*
* This will also notify the `onStop` listeners.
*/
public stop(): Interpreter<TContext, TStateSchema, TEvent> {
public stop(): Interpreter<TContext, TStateSchema, TEvent, TTypestate> {
for (const listener of this.listeners) {
this.listeners.delete(listener);
}
Expand Down Expand Up @@ -1320,7 +1320,7 @@ export function interpret<
TContext = DefaultContext,
TStateSchema extends StateSchema = any,
TEvent extends EventObject = EventObject,
TTypestate extends Typestate<TContext> = any
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
>(
machine: StateMachine<TContext, TStateSchema, TEvent, TTypestate>,
options?: Partial<InterpreterOptions>
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ export interface StateMachine<
TContext,
TStateSchema extends StateSchema,
TEvent extends EventObject,
TTypestate extends Typestate<TContext> = any
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
> extends StateNode<TContext, TStateSchema, TEvent, TTypestate> {
id: string;
states: StateNode<TContext, TStateSchema, TEvent>['states'];
Expand Down
6 changes: 3 additions & 3 deletions packages/xstate-fsm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function createUnchangedState<
export function createMachine<
TContext extends object,
TEvent extends EventObject = EventObject,
TState extends Typestate<TContext> = any
TState extends Typestate<TContext> = { value: any; context: TContext }
>(
fsmConfig: StateMachine.Config<TContext, TEvent, TState>,
options: {
Expand Down Expand Up @@ -194,7 +194,7 @@ export function createMachine<
const executeStateActions = <
TContext extends object,
TEvent extends EventObject = any,
TState extends Typestate<TContext> = any
TState extends Typestate<TContext> = { value: any; context: TContext }
>(
state: StateMachine.State<TContext, TEvent, TState>,
event: TEvent | InitEvent
Expand All @@ -203,7 +203,7 @@ const executeStateActions = <
export function interpret<
TContext extends object,
TEvent extends EventObject = EventObject,
TState extends Typestate<TContext> = any
TState extends Typestate<TContext> = { value: any; context: TContext }
>(
machine: StateMachine.Machine<TContext, TEvent, TState>
): StateMachine.Service<TContext, TEvent, TState> {
Expand Down
6 changes: 3 additions & 3 deletions packages/xstate-fsm/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export namespace StateMachine {
export interface Config<
TContext extends object,
TEvent extends EventObject,
TState extends Typestate<TContext> = any
TState extends Typestate<TContext> = { value: any; context: TContext }
> {
id?: string;
initial: string;
Expand All @@ -94,7 +94,7 @@ export namespace StateMachine {
TEvent extends EventObject,
TState extends Typestate<TContext>
> {
config: StateMachine.Config<TContext, TEvent>;
config: StateMachine.Config<TContext, TEvent, TState>;
initialState: State<TContext, TEvent, TState>;
transition: (
state: string | State<TContext, TEvent, TState>,
Expand All @@ -109,7 +109,7 @@ export namespace StateMachine {
export interface Service<
TContext extends object,
TEvent extends EventObject,
TState extends Typestate<TContext> = any
TState extends Typestate<TContext> = { value: any; context: TContext }
> {
send: (event: TEvent | TEvent['type']) => void;
subscribe: (
Expand Down
6 changes: 3 additions & 3 deletions packages/xstate-react/src/fsm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import useConstant from './useConstant';
const getServiceState = <
TContext extends object,
TEvent extends EventObject = EventObject,
TState extends Typestate<TContext> = any
TState extends Typestate<TContext> = { value: any; context: TContext }
>(
service: StateMachine.Service<TContext, TEvent, TState>
): StateMachine.State<TContext, TEvent, TState> => {
Expand All @@ -28,7 +28,7 @@ const getServiceState = <
export function useMachine<
TContext extends object,
TEvent extends EventObject = EventObject,
TState extends Typestate<TContext> = any
TState extends Typestate<TContext> = { value: any; context: TContext }
>(
stateMachine: StateMachine.Machine<TContext, TEvent, TState>,
options?: {
Expand Down Expand Up @@ -80,7 +80,7 @@ export function useMachine<
export function useService<
TContext extends object,
TEvent extends EventObject = EventObject,
TState extends Typestate<TContext> = any
TState extends Typestate<TContext> = { value: any; context: TContext }
>(
service: StateMachine.Service<TContext, TEvent, TState>
): [
Expand Down
2 changes: 1 addition & 1 deletion packages/xstate-react/src/useMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ interface UseMachineOptions<TContext, TEvent extends EventObject> {
export function useMachine<
TContext,
TEvent extends EventObject,
TTypestate extends Typestate<TContext> = any
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
>(
machine: StateMachine<TContext, any, TEvent, TTypestate>,
options: Partial<InterpreterOptions> &
Expand Down
8 changes: 5 additions & 3 deletions packages/xstate-react/src/useService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useActor } from './useActor';
import { ActorRef } from './types';

export function fromService<TContext, TEvent extends EventObject>(
service: Interpreter<TContext, any, TEvent, any>
service: Interpreter<TContext, any, TEvent>
): ActorRef<TEvent, State<TContext, TEvent>> {
const { machine } = service;
return {
Expand All @@ -19,11 +19,13 @@ export function fromService<TContext, TEvent extends EventObject>(
export function useService<
TContext,
TEvent extends EventObject,
TTypestate extends Typestate<TContext> = any
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
>(
service: Interpreter<TContext, any, TEvent, TTypestate>
): [State<TContext, TEvent, any, TTypestate>, Sender<TEvent>] {
const serviceActor = useMemo(() => fromService(service), [service]);

return useActor<TEvent, State<TContext, TEvent>>(serviceActor);
return useActor<TEvent, State<TContext, TEvent, any, TTypestate>>(
serviceActor
);
}
4 changes: 2 additions & 2 deletions packages/xstate-vue/src/fsm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
const getServiceValue = <
TContext extends object,
TEvent extends EventObject = EventObject,
TState extends Typestate<TContext> = any
TState extends Typestate<TContext> = { value: any; context: TContext }
>(
service: StateMachine.Service<TContext, TEvent, TState>
): StateMachine.State<TContext, TEvent, TState> => {
Expand Down Expand Up @@ -66,7 +66,7 @@ export function useMachine<
export function useService<
TContext extends object,
TEvent extends EventObject = EventObject,
TState extends Typestate<TContext> = any
TState extends Typestate<TContext> = { value: any; context: TContext }
>(
service:
| StateMachine.Service<TContext, TEvent, TState>
Expand Down
41 changes: 24 additions & 17 deletions packages/xstate-vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import {
Interpreter,
InterpreterOptions,
MachineOptions,
StateConfig
StateConfig,
Typestate
} from 'xstate';

interface UseMachineOptions<TContext, TEvent extends EventObject> {
Expand All @@ -29,15 +30,19 @@ interface UseMachineOptions<TContext, TEvent extends EventObject> {
state?: StateConfig<TContext, TEvent>;
}

export function useMachine<TContext, TEvent extends EventObject>(
machine: StateMachine<TContext, any, TEvent>,
export function useMachine<
TContext,
TEvent extends EventObject,
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
>(
machine: StateMachine<TContext, any, TEvent, TTypestate>,
options: Partial<InterpreterOptions> &
Partial<UseMachineOptions<TContext, TEvent>> &
Partial<MachineOptions<TContext, TEvent>> = {}
): {
state: Ref<State<TContext, TEvent>>;
send: Interpreter<TContext, any, TEvent>['send'];
service: Interpreter<TContext, any, TEvent>;
state: Ref<State<TContext, TEvent, any, TTypestate>>;
send: Interpreter<TContext, any, TEvent, TTypestate>['send'];
service: Interpreter<TContext, any, TEvent, TTypestate>;
} {
const {
context,
Expand Down Expand Up @@ -68,7 +73,7 @@ export function useMachine<TContext, TEvent extends EventObject>(
rehydratedState ? State.create(rehydratedState) : undefined
);

const state = shallowRef<State<TContext, TEvent>>(service.state);
const state = shallowRef(service.state);

onMounted(() => {
service.onTransition((currentState) => {
Expand All @@ -87,19 +92,21 @@ export function useMachine<TContext, TEvent extends EventObject>(
return { state, send: service.send, service };
}

export function useService<TContext, TEvent extends EventObject>(
export function useService<
TContext,
TEvent extends EventObject,
TTypestate extends Typestate<TContext> = { value: any; context: TContext }
>(
service:
| Interpreter<TContext, any, TEvent>
| Ref<Interpreter<TContext, any, TEvent>>
| Interpreter<TContext, any, TEvent, TTypestate>
| Ref<Interpreter<TContext, any, TEvent, TTypestate>>
): {
state: Ref<State<TContext, TEvent>>;
send: Interpreter<TContext, any, TEvent>['send'];
service: Ref<Interpreter<TContext, any, TEvent>>;
state: Ref<State<TContext, TEvent, any, TTypestate>>;
send: Interpreter<TContext, any, TEvent, TTypestate>['send'];
service: Ref<Interpreter<TContext, any, TEvent, TTypestate>>;
} {
const serviceRef = isRef(service)
? service
: shallowRef<Interpreter<TContext, any, TEvent>>(service);
const state = shallowRef<State<TContext, TEvent>>(serviceRef.value.state);
const serviceRef = isRef(service) ? service : shallowRef(service);
const state = shallowRef(serviceRef.value.state);

watch(
serviceRef,
Expand Down

0 comments on commit 8662e54

Please sign in to comment.