From 4cc341d293f63ff76659d3d47e44a09e90395f32 Mon Sep 17 00:00:00 2001 From: cevr Date: Tue, 13 Jan 2026 12:37:16 -0500 Subject: [PATCH] feat(core): add actions, guards, delays to SetupTypes interface Add type slots for actions, guards, and delays to the SetupTypes interface. This enables explicit type annotations for machines, which helps break TypeScript inference chains in large codebases that can cause performance issues and "excessively large and possibly infinite" type errors. Changes: - Add TAction, TGuard, TDelay type parameters to SetupTypes interface - Add actions, guards, delays properties to SetupTypes - Update MachineTypes to extend SetupTypes with new parameters - Update setup() to pass action/guard/delay types to SetupTypes - Add type tests for new functionality Co-Authored-By: Claude --- packages/core/src/setup.ts | 5 +- packages/core/src/types.ts | 17 ++-- packages/core/test/setup.types.test.ts | 110 +++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 7 deletions(-) diff --git a/packages/core/src/setup.ts b/packages/core/src/setup.ts index 8d2ce7a6a0..670e7d650d 100644 --- a/packages/core/src/setup.ts +++ b/packages/core/src/setup.ts @@ -359,7 +359,10 @@ export function setup< TInput, TOutput, TEmitted, - TMeta + TMeta, + ToParameterizedObject, + ToParameterizedObject, + TDelay >; actors?: { // union here enforces that all configured children have to be provided in actors diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 7e516e7948..1bff49f439 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1396,7 +1396,10 @@ export interface SetupTypes< TInput, TOutput, TEmitted extends EventObject, - TMeta extends MetaObject + TMeta extends MetaObject, + TAction extends ParameterizedObject = ParameterizedObject, + TGuard extends ParameterizedObject = ParameterizedObject, + TDelay extends string = string > { context?: TContext; events?: TEvent; @@ -1406,6 +1409,9 @@ export interface SetupTypes< output?: TOutput; emitted?: TEmitted; meta?: TMeta; + actions?: TAction; + guards?: TGuard; + delays?: TDelay; } export interface MachineTypes< @@ -1430,13 +1436,12 @@ export interface MachineTypes< TInput, TOutput, TEmitted, - TMeta + TMeta, + TAction, + TGuard, + TDelay > { actors?: TActor; - actions?: TAction; - guards?: TGuard; - delays?: TDelay; - meta?: TMeta; } export interface HistoryStateNode diff --git a/packages/core/test/setup.types.test.ts b/packages/core/test/setup.types.test.ts index 619ad9d166..df4839facb 100644 --- a/packages/core/test/setup.types.test.ts +++ b/packages/core/test/setup.types.test.ts @@ -3159,4 +3159,114 @@ describe('type-bound actions', () => { entry: spawn }); }); + + describe('type declarations in types', () => { + it('should infer action types from implementations', () => { + const { createMachine } = setup({ + types: {} as { + context: { count: number }; + events: { type: 'INC' }; + }, + actions: { + increment: assign(({ context }) => ({ count: context.count + 1 })), + log: (_, params: { message: string }) => console.log(params.message) + } + }); + + createMachine({ + context: { count: 0 }, + initial: 'idle', + states: { + idle: { + on: { + INC: { + actions: 'increment' + } + } + } + } + }); + }); + + it('should infer guard types from implementations', () => { + const { createMachine } = setup({ + types: {} as { + context: { count: number }; + events: { type: 'INC' }; + }, + guards: { + isPositive: ({ context }) => context.count > 0, + isGreaterThan: ({ context }, params: { value: number }) => + context.count > params.value + } + }); + + createMachine({ + context: { count: 0 }, + initial: 'idle', + states: { + idle: { + on: { + INC: { + guard: 'isPositive', + target: 'idle' + } + } + } + } + }); + }); + + it('should infer delay types from implementations', () => { + const { createMachine } = setup({ + types: {} as { + context: { timeout: number }; + events: { type: 'START' }; + }, + delays: { + shortDelay: 100, + longDelay: ({ context }) => context.timeout + } + }); + + createMachine({ + context: { timeout: 1000 }, + initial: 'idle', + states: { + idle: { + after: { + shortDelay: 'active' + } + }, + active: {} + } + }); + }); + + it('SetupTypes should include actions, guards, and delays slots', () => { + // This test verifies that SetupTypes has the action/guard/delay type parameters + // which enables explicit type annotations for breaking inference chains + type MySetupTypes = { + context: { count: number }; + events: { type: 'INC' }; + actions: { type: 'increment'; params: undefined }; + guards: { type: 'isValid'; params: undefined }; + delays: 'timeout'; + }; + + // The types can be used for explicit annotations + const _types: MySetupTypes = { + context: { count: 0 }, + events: { type: 'INC' }, + actions: { type: 'increment', params: undefined }, + guards: { type: 'isValid', params: undefined }, + delays: 'timeout' + }; + + // Verify the shape is correct + expect(_types.actions.type).toBe('increment'); + expect(_types.guards.type).toBe('isValid'); + expect(_types.delays).toBe('timeout'); + }); + }); });