Skip to content
Version: XState v4

Typegen

You can automatically generate intelligent typings for XState using our VS Code extension or our CLI.

How to get started with VS Code

All you need to do is to install the Stately XState extension.

How to get started with the CLI (other editors)

Install the CLI and run the xstate typegen command with the --watch flag.

Typegen in action

Next try to create a machine, make sure to set the schema attributes:

import { createMachine } from 'xstate';

const machine = createMachine({
schema: {
context: {} as { value: string },
events: {} as { type: 'FOO'; value: string } | { type: 'BAR' },
},
initial: 'a',
states: {
a: {
on: {
FOO: {
actions: 'consoleLogValue',
target: 'b',
},
},
},
b: {
entry: 'consoleLogValueAgain',
},
},
});
  1. Add tsTypes: {} to the machine and save the file:
const machine = createMachine({
tsTypes: {},
schema: {
context: {} as { value: string },
events: {} as { type: 'FOO'; value: string } | { type: 'BAR' },
},
initial: 'a',
states: {
a: {},
b: {},
},
});
  1. The extension should automatically add a generic to the machine:
const machine = createMachine({
tsTypes: {} as import('./filename.typegen').Typegen0,
/* ... */
});
  1. Add a second parameter into the createMachine call. This second parameter is where you implement the machine’s actions, actors, guards and delays.
const machine = createMachine(
{
/* ... */
},
{
actions: {
consoleLogValue: (context, event) => {
// Wow! event is typed to { type: 'FOO' }
console.log(event.value);
},
consoleLogValueAgain: (context, event) => {
// Wow! event is typed to { type: 'FOO' }
console.log(event.value);
},
},
},
);

Now the events in the options are strongly typed to the events that cause the action to be triggered, including actions, guards, actors and delays.

You’ll also notice that state.matches, tags and other parts of the machine are now type-safe.

Typing promise actors

You can use the generated types to specify the return type of promise-based actors by using the actors schema property:

import { createMachine } from 'xstate';

createMachine(
{
schema: {
// `actors` in v5
services: {} as {
myActor: {
// The data that gets returned from the actor
data: { id: string };
};
},
},
invoke: {
src: 'myActor',
onDone: {
actions: 'consoleLogId',
},
},
},
{
// `actors` in v5
services: {
myActor: async () => {
// This return type is now type-safe
return {
id: '1',
};
},
},
actions: {
consoleLogId: (context, event) => {
// This event type is now type-safe
console.log(event.data.id);
},
},
},
);

Typegen best practices

Below are some recommendations to help you get the most out of using typegen.

Use named actions, guards and actors

We recommend using named actions, guards and actors instead of inline actions, guards and actors.

Named actions, actors and guards allow for:

  • Better visualization with the names appearing in the statechart
  • Easier-to-understand code
  • Overrides in useMachine or machine.withConfig

The following example is optimal:

createMachine(
{
entry: ['sayHello'],
},
{
actions: {
sayHello: () => {
console.log('Hello!');
},
},
},
);

The following example is useful but less optimal:

createMachine({
entry: [
() => {
console.log('Hello!');
},
],
});

The generated files

We recommend you gitignore the generated files (*filename*.typegen.ts) from your repository.

You can use the CLI to regenerate them on CI (Continuous Integration), for instance, via a postinstall script:

{
"scripts": {
"postinstall": "xstate typegen \"./src/**/*.ts?(x)\""
}
}

Don’t use enums

Enums were a common pattern used with XState TypeScript and were often used to declare state names as follows:

enum States {
A,
B,
}

createMachine({
initial: States.A,
states: {
[States.A]: {},
[States.B]: {},
},
});

You can then check state.matches(States.A) on the resulting machine, which allows for type-safe checks of state names.

With typegen, using enums is no longer necessary as all state.matches types are type-safe. Enums are currently not supported by our static analysis tool. We’re unlikely to support enums with typegen due to the complexity they add for comparatively little gain.

Instead of enums, use typegen and rely on the strength of the type-safety provided.

Nesting typegen files

When you use typegen, you'll notice that it generates new type files. If you use VS Code we have made it easy for you to nest these files. Our extension will automatically ask you if you want to enable nesting. If you want to know more about file-nesting, you can read the blog post where we introduced nesting typegen files.

Known limitations

There are a few known limitations with typegen, which we are working to fix.

“Always” transitions and raised events

Typegen might incorrectly annotate actions, actors, guards and delays if they are called “in response” to always transitions or raised events. We are working on fixing this, both in XState and in the typegen.