Skip to main content
xstate-typescript Covers TypeScript patterns for type-safe XState v5 machines. Use when setting up typed machines with setup(), typing context/events/input/output, using type-bound helpers (v5.22+), assertEvent(), or type helpers like ActorRefFrom and SnapshotFrom. Requires TypeScript 5.0+.
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/IlyaGulya/claude-marketplace --skill xstate-typescript명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... Analyzes CLAUDE.md, AGENTS.md, and similar repository context files for effectiveness based on peer-reviewed research. Identifies instructions that hurt agent performance, redundant content, and unnecessary requirements. Use when reviewing a CLAUDE.md, auditing an AGENTS.md, optimizing context files, or when the user says "lint my CLAUDE.md", "review my AGENTS.md", "is my context file good", "optimize my context file", or "check my CLAUDE.md".
name xstate-typescript description Covers TypeScript patterns for type-safe XState v5 machines. Use when setting up typed machines with setup(), typing context/events/input/output, using type-bound helpers (v5.22+), assertEvent(), or type helpers like ActorRefFrom and SnapshotFrom. Requires TypeScript 5.0+.
XState v5 TypeScript
Prerequisites
TypeScript 5.0+ (latest recommended)
strictNullChecks: true in tsconfig.json (strongly recommended)
skipLibCheck: true in tsconfig.json (recommended)
{
"compilerOptions" : {
"strictNullChecks" : true ,
"skipLibCheck" : true
}
}
The setup() Pattern
The setup() function is the primary way to create type-safe machines:
import { setup } from 'xstate' ;
const machine = setup ({
types : {
context : {} as {
: ;
: | ;
: | ;
},
: {}
| { : ; : }
| { : }
| { : },
: {} {
: ;
},
: {} {
: ;
},
: {} {
: ;
},
: {} | ,
},
: { },
: { },
: { },
: { },
}). ({
});
userId
string
data
User
null
error
string
null
events
as
type
'FETCH'
userId
string
type
'RETRY'
type
'RESET'
input
as
initialUserId
string
output
as
result
User
children
as
fetcher
'fetchUser'
tags
as
'loading'
'error'
actions
guards
actors
delays
createMachine
The {} as Type pattern is a TypeScript idiom for providing type information without runtime values.
Typing Context and Events const machine = setup ({
types : {
context : {} as {
count : number ;
items : string [];
user : { name : string ; email : string } | null ;
},
events : {} as
| { type : 'increment' ; value : number }
| { type : 'item.add' ; item : string }
| { type : 'item.remove' ; index : number }
| { type : 'user.set' ; user : { name : string ; email : string } },
},
}).createMachine ({
context : {
count : 0 ,
items : [],
user : null ,
},
on : {
increment : {
actions : assign ({
count : ({ context, event } ) => context.count + event.value ,
}),
},
'item.add' : {
actions : assign ({
items : ({ context, event } ) => [...context.items , event.item ],
}),
},
},
});
Typing Input and Output For reusable machines that accept configuration and produce results:
const searchMachine = setup ({
types : {
context : {} as {
query : string ;
results : SearchResult [];
},
input : {} as {
initialQuery : string ;
maxResults : number ;
},
output : {} as {
results : SearchResult [];
totalCount : number ;
},
},
}).createMachine ({
context : ({ input } ) => ({
query : input.initialQuery ,
results : [],
}),
states : {
done : {
type : 'final' ,
},
},
output : ({ context } ) => ({
results : context.results ,
totalCount : context.results .length ,
}),
});
const actor = createActor (searchMachine, {
input : { initialQuery : 'xstate' , maxResults : 10 },
});
Typing Actions and Guards
Parameterized Actions const machine = setup ({
actions : {
notify : (_, params : { message: string ; level: 'info' | 'error' } ) => {
showNotification (params.message , params.level );
},
},
guards : {
isAboveThreshold : (_, params : { value: number ; threshold: number } ) => {
return params.value > params.threshold ;
},
},
}).createMachine ({
on : {
SUCCESS : {
actions : {
type : 'notify' ,
params : { message : 'Done!' , level : 'info' },
},
},
CHECK : {
guard : {
type : 'isAboveThreshold' ,
params : ({ context } ) => ({
value : context.count ,
threshold : 100 ,
}),
},
},
},
});
Type-Bound Helpers (v5.22+) Create actions in separate files while maintaining full type safety:
import { setup } from 'xstate' ;
export const machineSetup = setup ({
types : {
context : {} as { count : number ; items : string [] },
events : {} as
| { type : 'increment' }
| { type : 'addItem' ; item : string }
| { type : 'reset' },
emitted : {} as { type : 'COUNT_CHANGED' ; count : number },
},
});
import { machineSetup } from './machineSetup' ;
export const incrementCount = machineSetup.assign ({
count : ({ context } ) => context.count + 1 ,
});
export const addItem = machineSetup.assign ({
items : ({ context, event } ) => [...context.items , event.item ],
});
export const raiseReset = machineSetup.raise ({ type : 'reset' });
export const emitChange = machineSetup.emit (({ context } ) => ({
type : 'COUNT_CHANGED' ,
count : context.count ,
}));
export const logState = machineSetup.createAction (({ context, event } ) => {
console .log ("Count: " + context.count + ", Event: " + event.type );
});
import { machineSetup } from './machineSetup' ;
import { incrementCount, addItem, logState } from './actions' ;
export const machine = machineSetup.createMachine ({
context : { count : 0 , items : [] },
initial : 'active' ,
states : {
active : {
entry : logState,
on : {
increment : { actions : incrementCount },
addItem : { actions : addItem },
},
},
},
});
Modular State Configs (v5.21+) Split large machines across files with createStateConfig():
export const appSetup = setup ({
types : {
context : {} as AppContext ,
events : {} as AppEvent ,
},
actions : { },
actors : { },
});
import { appSetup } from '../setup' ;
export const editingState = appSetup.createStateConfig ({
entry : { type : 'loadDraft' },
on : {
SAVE : { target : 'saving' , actions : { type : 'saveDraft' } },
VALIDATE : { target : 'validating' },
},
});
import { appSetup } from './setup' ;
import { editingState } from './states/editing' ;
export const appMachine = appSetup.createMachine ({
initial : 'editing' ,
states : {
editing : editingState,
validating : { },
saving : { },
},
});
Type Helpers
ActorRefFrom Get a typed actor reference from actor logic:
import { type ActorRefFrom } from 'xstate' ;
type MyActorRef = ActorRefFrom <typeof myMachine>;
interface Props {
actorRef : ActorRefFrom <typeof formMachine>;
}
SnapshotFrom Get a typed snapshot from actor logic or actor ref:
import { type SnapshotFrom } from 'xstate' ;
type MySnapshot = SnapshotFrom <typeof myMachine>;
function renderState (snapshot : SnapshotFrom <typeof myMachine> ) {
snapshot.context ;
snapshot.value ;
}
EventFromLogic Get all event types from actor logic:
import { type EventFromLogic } from 'xstate' ;
type MyEvent = EventFromLogic <typeof myMachine>;
OutputFrom Get the output type from actor logic:
import { type OutputFrom } from 'xstate' ;
type MyOutput = OutputFrom <typeof myMachine>;
assertEvent() Narrow event types in actions/guards where the event type is a union:
import { assertEvent } from 'xstate' ;
const machine = setup ({
types : {
events : {} as
| { type : 'greet' ; name : string }
| { type : 'submit' ; data : FormData }
| { type : 'cancel' },
},
}).createMachine ({
states : {
greeting : {
entry : ({ event } ) => {
assertEvent (event, 'greet' );
console .log (event.name .toUpperCase ());
},
},
processing : {
exit : ({ event } ) => {
assertEvent (event, ['greet' , 'submit' ]);
},
},
},
});
Prefer dynamic params over assertEvent — params are more reusable:
actions : {
greetUser : (_, params : { name: string } ) => {
console .log ("Hello, " + params.name + "!" );
},
}
Common Patterns
Typing fromPromise import { fromPromise } from 'xstate' ;
interface User { id : string ; name : string }
const fetchUser = fromPromise<User , { userId : string }>(async ({ input }) => {
const res = await fetch (`/api/users/${input.userId} ` );
return res.json () as Promise <User >;
});
invoke : {
src : 'fetchUser' ,
input : ({ context } ) => ({ userId : context.userId }),
onDone : {
actions : assign ({
user : ({ event } ) => event.output ,
}),
},
}
Typing .provide()
const testMachine = machine.provide ({
actions : {
notify : (_, params : { message: string ; level: 'info' | 'error' } ) => {
console .log (params.message );
},
},
actors : {
fetchUser : fromPromise<User , { userId : string }>(async ({ input }) => {
return mockUser;
}),
},
});
Context Factory with Input const machine = setup ({
types : {
context : {} as {
userId : string ;
preferences : UserPrefs ;
isReady : boolean ;
},
input : {} as {
userId : string ;
preferences ?: Partial <UserPrefs >;
},
},
}).createMachine ({
context : ({ input } ) => ({
userId : input.userId ,
preferences : {
theme : 'light' ,
language : 'en' ,
...input.preferences ,
},
isReady : false ,
}),
});