Skip to content

infered typehint for state machine properties 'on' and 'initial' #5217

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/many-tips-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': minor
---

Added a new flag 'scriptType' to state machine which provides different levels of typehint for states 'initial' and 'on' properties.
13 changes: 10 additions & 3 deletions packages/core/src/createMachine.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { StateMachine } from './StateMachine.ts';
import { ResolvedStateMachineTypes, TODO } from './types.ts';
import { ResolvedStateMachineTypes, StrictType, TODO } from './types.ts';
import {
AnyActorRef,
EventObject,
Expand Down Expand Up @@ -84,6 +84,8 @@ export function createMachine<
TOutput extends NonReducibleUnknown,
TEmitted extends EventObject,
TMeta extends MetaObject,
TStates extends Record<string, any>,
TStrictType extends StrictType,
// it's important to have at least one default type parameter here
// it allows us to benefit from contextual type instantiation as it makes us to pass the hasInferenceCandidatesOrDefault check in the compiler
// we should be able to remove this when we start inferring TConfig, with it we'll always have an inference candidate
Expand Down Expand Up @@ -115,8 +117,13 @@ export function createMachine<
TInput,
TOutput,
TEmitted,
TMeta
>,
TMeta,
TStates,
Extract<keyof TStates, string>,
TStrictType
> & {
strictType?: TStrictType;
},
implementations?: InternalMachineImplementations<
ResolvedStateMachineTypes<
TContext,
Expand Down
77 changes: 59 additions & 18 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ export interface StateValueMap {
*/
export type StateValue = string | StateValueMap;

export type TransitionTarget = SingleOrArray<string>;
export type TransitionTarget<T extends string = string> = SingleOrArray<T>;

export interface TransitionConfig<
TContext extends MachineContext,
Expand All @@ -317,7 +317,9 @@ export interface TransitionConfig<
TGuard extends ParameterizedObject,
TDelay extends string,
TEmitted extends EventObject = EventObject,
TMeta extends MetaObject = MetaObject
TMeta extends MetaObject = MetaObject,
TParent extends string = string,
TStrictType extends StrictType = undefined
> {
guard?: Guard<TContext, TExpressionEvent, undefined, TGuard>;
actions?: Actions<
Expand All @@ -332,7 +334,7 @@ export interface TransitionConfig<
TEmitted
>;
reenter?: boolean;
target?: TransitionTarget | undefined;
target?: TransitionTarget<StrictSelect<TParent, TStrictType>> | undefined;
meta?: TMeta;
description?: string;
}
Expand Down Expand Up @@ -513,9 +515,11 @@ export type StatesConfig<
TTag extends string,
TOutput,
TEmitted extends EventObject,
TMeta extends MetaObject
TMeta extends MetaObject,
TStates extends Record<string, any> = Record<string, any>,
TStrictType extends StrictType = undefined
> = {
[K in string]: StateNodeConfig<
[K in keyof TStates]: StateNodeConfig<
TContext,
TEvent,
TActor,
Expand All @@ -525,7 +529,10 @@ export type StatesConfig<
TTag,
TOutput,
TEmitted,
TMeta
TMeta,
TStates[K],
Extract<keyof TStates, string>,
TStrictType
>;
};

Expand All @@ -536,7 +543,7 @@ export type StatesDefinition<
[K in string]: StateNodeDefinition<TContext, TEvent>;
};

export type TransitionConfigTarget = string | undefined;
export type TransitionConfigTarget<T extends string = string> = T | undefined;

export type TransitionConfigOrTarget<
TContext extends MachineContext,
Expand All @@ -547,9 +554,11 @@ export type TransitionConfigOrTarget<
TGuard extends ParameterizedObject,
TDelay extends string,
TEmitted extends EventObject,
TMeta extends MetaObject
TMeta extends MetaObject,
TParent extends string = string,
TStrictType extends StrictType = undefined
> = SingleOrArray<
| TransitionConfigTarget
| TransitionConfigTarget<StrictSelect<TParent, TStrictType>>
| TransitionConfig<
TContext,
TExpressionEvent,
Expand All @@ -559,7 +568,9 @@ export type TransitionConfigOrTarget<
TGuard,
TDelay,
TEmitted,
TMeta
TMeta,
TParent,
TStrictType
>
>;

Expand All @@ -571,7 +582,9 @@ export type TransitionsConfig<
TGuard extends ParameterizedObject,
TDelay extends string,
TEmitted extends EventObject,
TMeta extends MetaObject
TMeta extends MetaObject,
TParent extends string = string,
TStrictType extends StrictType = undefined
> = {
[K in EventDescriptor<TEvent>]?: TransitionConfigOrTarget<
TContext,
Expand All @@ -582,7 +595,9 @@ export type TransitionsConfig<
TGuard,
TDelay,
TEmitted,
TMeta
TMeta,
TParent,
TStrictType
>;
};

Expand Down Expand Up @@ -864,12 +879,16 @@ export interface StateNodeConfig<
TTag extends string,
_TOutput,
TEmitted extends EventObject,
TMeta extends MetaObject
TMeta extends MetaObject,
TStates extends Record<string, any> = Record<string, any>,
TParent extends string = string,
TStrictType extends StrictType = undefined
> {
/** The initial state transition. */

initial?:
| InitialTransitionConfig<TContext, TEvent, TActor, TAction, TGuard, TDelay>
| string
| StrictSelect<NoInfer<Extract<keyof TStates, string>>, TStrictType>
| undefined;
/**
* The type of this state node:
Expand Down Expand Up @@ -901,7 +920,9 @@ export interface StateNodeConfig<
TTag,
NonReducibleUnknown,
TEmitted,
TMeta
TMeta,
TStates,
TStrictType
>
| undefined;
/**
Expand Down Expand Up @@ -929,7 +950,9 @@ export interface StateNodeConfig<
TGuard,
TDelay,
TEmitted,
TMeta
TMeta,
TParent,
TStrictType
>;
/** The action(s) to be executed upon entering the state node. */
entry?: Actions<
Expand Down Expand Up @@ -1351,7 +1374,10 @@ export type MachineConfig<
TInput = any,
TOutput = unknown,
TEmitted extends EventObject = EventObject,
TMeta extends MetaObject = MetaObject
TMeta extends MetaObject = MetaObject,
TStates extends Record<string, any> = Record<string, any>,
TParent extends string = string,
TStrictType extends StrictType = undefined
> = (Omit<
StateNodeConfig<
DoNotInfer<TContext>,
Expand All @@ -1363,13 +1389,17 @@ export type MachineConfig<
DoNotInfer<TTag>,
DoNotInfer<TOutput>,
DoNotInfer<TEmitted>,
DoNotInfer<TMeta>
DoNotInfer<TMeta>,
TStates,
TParent,
TStrictType
>,
'output'
> & {
/** The initial context (extended state) */
/** The machine's own version. */
version?: string;

// TODO: make it conditionally required
output?: Mapper<TContext, DoneStateEvent, TOutput, TEvent> | TOutput;
}) &
Expand Down Expand Up @@ -2678,3 +2708,14 @@ export type BuiltinActionResolution = [
NonReducibleUnknown, // params
UnknownAction[] | undefined
];

export type StrictType = 'strict' | 'loose' | undefined;

type StrictSelect<
elements extends string,
strict extends StrictType = undefined
> = strict extends 'strict'
? elements
: strict extends 'loose'
? elements | (string & {})
: string;