Skip to content

Commit

Permalink
Introduce TExpressionEvent generic (#3694)
Browse files Browse the repository at this point in the history
* Introduce `TExpressionEvent` generic

# Clean up

# fixed types

# fix more problems manifested in tests

# fixed an immer problem

* Fix things

* cleanup

* add changeset
  • Loading branch information
Andarist committed Feb 13, 2023
1 parent 430986c commit fd58905
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 175 deletions.
7 changes: 7 additions & 0 deletions .changeset/shy-donkeys-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'xstate': minor
---

All actions received a new generic: `TExpressionEvent`. To type things more correctly and allow TS to infer things better we need to distinguish between all events accepted by a machine (`TEvent`) and the event type that actions are "called" with (`TExpressionEvent`).

It's best to rely on type inference so you shouldn't have to specify this generic manually all over the place.
6 changes: 3 additions & 3 deletions packages/core/src/StateNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ class StateNode<
})
.concat({
type: 'state_done',
actions: doneEvents.map((event) => raise(event)) as Array<
actions: doneEvents.map((event) => raise(event as any)) as Array<
ActionObject<TContext, TEvent>
>
});
Expand Down Expand Up @@ -1317,7 +1317,7 @@ class StateNode<
);

const [raisedEvents, nonRaisedActions] = partition(
resolvedActions,
resolvedActions as any,
isRaisableAction
);

Expand Down Expand Up @@ -1435,7 +1435,7 @@ class StateNode<
const raisedEvent = raisedEvents.shift()!;
maybeNextState = this.resolveRaisedTransition(
maybeNextState,
raisedEvent._event,
raisedEvent._event as any,
_event,
predictableExec
);
Expand Down
57 changes: 34 additions & 23 deletions packages/core/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
AnyActorRef,
PredictableActionArgumentsExec,
RaiseActionOptions,
NoInfer,
BaseActionObject,
LowInfer
} from './types';
Expand Down Expand Up @@ -159,24 +160,32 @@ export function toActivityDefinition<TContext, TEvent extends EventObject>(
*
* @param eventType The event to raise.
*/
export function raise<TContext, TEvent extends EventObject>(
event: Event<TEvent> | SendExpr<TContext, TEvent, TEvent>,
options?: RaiseActionOptions<TContext, TEvent>
): RaiseAction<TContext, TEvent> {
export function raise<
TContext,
TExpressionEvent extends EventObject,
TEvent extends EventObject = TExpressionEvent
>(
event: NoInfer<Event<TEvent>> | SendExpr<TContext, TExpressionEvent, TEvent>,
options?: RaiseActionOptions<TContext, TExpressionEvent>
): RaiseAction<TContext, TExpressionEvent, TEvent> {
return {
type: actionTypes.raise,
event: typeof event === 'function' ? event : toEventObject<TEvent>(event),
event: typeof event === 'function' ? event : toEventObject<any>(event),
delay: options ? options.delay : undefined,
id: options?.id
} as any;
}

export function resolveRaise<TContext, TEvent extends EventObject>(
action: RaiseAction<TContext, TEvent>,
export function resolveRaise<
TContext,
TEvent extends EventObject,
TExpressionEvent extends EventObject
>(
action: RaiseAction<TContext, TExpressionEvent, TEvent>,
ctx: TContext,
_event: SCXML.Event<TEvent>,
_event: SCXML.Event<TExpressionEvent>,
delaysMap?: DelayFunctionMap<TContext, TEvent>
): RaiseActionObject<TContext, TEvent> {
): RaiseActionObject<TContext, TExpressionEvent, TEvent> {
const meta = {
_event
};
Expand All @@ -190,7 +199,7 @@ export function resolveRaise<TContext, TEvent extends EventObject>(
if (isString(action.delay)) {
const configDelay = delaysMap && delaysMap[action.delay];
resolvedDelay = isFunction(configDelay)
? configDelay(ctx, _event.data, meta)
? configDelay(ctx, _event.data as any, meta as any)
: configDelay;
} else {
resolvedDelay = isFunction(action.delay)
Expand Down Expand Up @@ -222,13 +231,13 @@ export function send<
TEvent extends EventObject,
TSentEvent extends EventObject = AnyEventObject
>(
event: Event<TSentEvent> | SendExpr<TContext, TEvent, TSentEvent>,
event: Event<AnyEventObject> | SendExpr<TContext, TEvent, AnyEventObject>,
options?: SendActionOptions<TContext, TEvent>
): SendAction<TContext, TEvent, TSentEvent> {
return {
to: options ? options.to : undefined,
type: actionTypes.send,
event: isFunction(event) ? event : toEventObject<TSentEvent>(event),
event: isFunction(event) ? event : toEventObject(event),
delay: options ? options.delay : undefined,
// TODO: don't auto-generate IDs here like that
// there is too big chance of the ID collision
Expand All @@ -237,7 +246,7 @@ export function send<
? options.id
: isFunction(event)
? event.name
: (getEventType<TSentEvent>(event) as string)
: (getEventType(event) as string)
} as any;
}

Expand Down Expand Up @@ -298,7 +307,7 @@ export function sendParent<
TEvent extends EventObject,
TSentEvent extends EventObject = AnyEventObject
>(
event: Event<TSentEvent> | SendExpr<TContext, TEvent, TSentEvent>,
event: Event<AnyEventObject> | SendExpr<TContext, TEvent, AnyEventObject>,
options?: SendActionOptions<TContext, TEvent>
): SendAction<TContext, TEvent, TSentEvent> {
return send<TContext, TEvent, TSentEvent>(event, {
Expand Down Expand Up @@ -346,11 +355,9 @@ export function sendTo<
export function sendUpdate<TContext, TEvent extends EventObject>(): SendAction<
TContext,
TEvent,
{ type: ActionTypes.Update }
AnyEventObject
> {
return sendParent<TContext, TEvent, { type: ActionTypes.Update }>(
actionTypes.update
);
return sendParent(actionTypes.update);
}

/**
Expand Down Expand Up @@ -495,11 +502,15 @@ export function resolveStop<TContext, TEvent extends EventObject>(
*
* @param assignment An object that represents the partial context to update.
*/
export const assign = <TContext, TEvent extends EventObject = EventObject>(
export const assign = <
TContext,
TExpressionEvent extends EventObject = EventObject,
TEvent extends EventObject = TExpressionEvent
>(
assignment:
| Assigner<LowInfer<TContext>, TEvent>
| PropertyAssigner<LowInfer<TContext>, TEvent>
): AssignAction<TContext, TEvent> => {
| Assigner<LowInfer<TContext>, TExpressionEvent>
| PropertyAssigner<LowInfer<TContext>, TExpressionEvent>
): AssignAction<TContext, TExpressionEvent, TEvent> => {
return {
type: actionTypes.assign,
assignment
Expand Down Expand Up @@ -722,7 +733,7 @@ export function resolveActions<TContext, TEvent extends EventObject>(
machine.options.delays as any
);
if (predictableExec && typeof raisedAction.delay === 'number') {
predictableExec(raisedAction, updatedContext, _event);
predictableExec(raisedAction as any, updatedContext, _event);
}
return raisedAction;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ export class Interpreter<
actionFunctionMap = this.machine.options.actions
): void => {
const actionOrExec =
action.exec || getActionFunction(action.type, actionFunctionMap);
action.exec || getActionFunction(action.type, actionFunctionMap as any);
const exec = isFunction(actionOrExec)
? actionOrExec
: actionOrExec
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/model.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ export interface Model<
| Assigner<TContext, SimplisticExtractEvent<TEvent, TEventType>>
| PropertyAssigner<TContext, SimplisticExtractEvent<TEvent, TEventType>>,
eventType?: TEventType
) => AssignAction<TContext, SimplisticExtractEvent<TEvent, TEventType>>;
) => AssignAction<
TContext,
SimplisticExtractEvent<TEvent, TEventType>,
TEvent
>;
events: Prop<TModelCreators, 'events'>;
actions: Prop<TModelCreators, 'actions'>;
reset: () => AssignAction<TContext, any>;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/scxml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ function mapAction<
>(element: XMLElement): ActionObject<TContext, TEvent> {
switch (element.name) {
case 'raise': {
return actions.raise<TContext, TEvent>(
return actions.raise<TContext, TEvent, TEvent>(
element.attributes!.event! as string
);
}
Expand Down

0 comments on commit fd58905

Please sign in to comment.