Skip to content
This repository has been archived by the owner on Mar 5, 2023. It is now read-only.

Added support for Typestates #28

Closed
wants to merge 3 commits into from
Closed
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
13 changes: 13 additions & 0 deletions .changeset/rich-spoons-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"xstate-codegen": minor
---

Added support for typestates in createMachine. You can now pass:

```ts
type State = { value: 'idle'; context: 'some-context' };

const machine = createMachine<Context, Event, State, 'machineId'>({});
```

This will be inferred inside options passed to the machine, and when you call `state.matches` after interpreting the machine.
128 changes: 128 additions & 0 deletions packages/xstate-compiled/examples/typeStatesTest.machine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { createMachine, interpret } from '@xstate/compiled';
import { useMachine } from '@xstate/compiled/react';

type Context = 'red-context' | 'green-context';

type Event = { type: 'DUMMY_TYPE' };

type State =
| { value: 'green'; context: 'green-context' }
| {
value: 'red';
context: 'red-context';
};

const machine = createMachine<Context, Event, State, 'typeStatesTest'>({
initial: 'green',
states: {
red: {
entry: ['redAction'],
invoke: {
src: 'redService',
onDone: {
cond: 'redCond',
},
},
},
green: {
entry: ['greenAction'],
invoke: {
src: 'greenService',
onDone: {
cond: 'greenCond',
},
},
},
},
});

const service = interpret(machine, {
actions: {
greenAction: (context) => {
// @ts-expect-error
context === 'red-context';
context === 'green-context';
},
redAction: (context) => {
// @ts-expect-error
context === 'green-context';
context === 'red-context';
},
},
guards: {
greenCond: (context) => {
// @ts-expect-error
context === 'red-context';
return context === 'green-context';
},
redCond: (context) => {
// @ts-expect-error
context === 'green-context';
return context === 'red-context';
},
},
services: {
greenService: async (context) => {
// @ts-expect-error
context === 'red-context';
context === 'green-context';
},
redService: async (context) => {
// @ts-expect-error
context === 'green-context';
context === 'red-context';
},
},
}).start();

if (service.state.matches('green')) {
// @ts-expect-error
service.state.context === 'red-context';
}

const useThisMachine = () => {
const [state, dispatch] = useMachine(machine, {
actions: {
greenAction: (context) => {
// @ts-expect-error
context === 'red-context';
context === 'green-context';
},
redAction: (context) => {
// @ts-expect-error
context === 'green-context';
context === 'red-context';
},
},
guards: {
greenCond: (context) => {
// @ts-expect-error
context === 'red-context';
return context === 'green-context';
},
redCond: (context) => {
// @ts-expect-error
context === 'green-context';
return context === 'red-context';
},
},
services: {
greenService: async (context) => {
// @ts-expect-error
context === 'red-context';
context === 'green-context';
},
redService: async (context) => {
// @ts-expect-error
context === 'green-context';
context === 'red-context';
},
},
});

if (state.matches('green')) {
// @ts-expect-error
state.context === 'red-context';
state.value === 'green';
}
};
22 changes: 22 additions & 0 deletions packages/xstate-compiled/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,28 @@ type Event = { type: 'DUMMY_TYPE' };
const machine = Machine<Context, Event, 'uniqueId'>({});
```

### Usage with React

```ts
import { useMachine } from '@xstate/compiled/react';
import { machine } from './myMachine.machine';

const [state, dispatch] = useMachine(machine, {
// all options in here will be type checked
});
```

### Usage with Interpret

```ts
import { interpret } from '@xstate/compiled';
import { machine } from './myMachine.machine';

const service = interpret(machine, {
// all options in here will be type checked
});
```

## Options

### Once
Expand Down
13 changes: 8 additions & 5 deletions packages/xstate-compiled/src/extractMachines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,16 @@ const getMachineId = (
) => {
const { typeParameters } = callExpression;

const lastParameter =
typeParameters?.params?.[typeParameters?.params?.length - 1];

if (
!typeParameters ||
!typeParameters.params[2] ||
!t.isTSLiteralType(typeParameters.params[2]) ||
!t.isStringLiteral(typeParameters.params[2].literal)
!lastParameter ||
!t.isTSLiteralType(lastParameter) ||
!t.isStringLiteral(lastParameter.literal)
) {
console.log('You must pass three type arguments to your machine.');
console.log('You must pass an ID property to your machine');
console.log();
console.log('For instance:');
console.log(
Expand All @@ -110,7 +113,7 @@ const getMachineId = (
console.log();
throw new Error('You must pass three type arguments to your machine.');
}
return typeParameters.params[2].literal.value;
return lastParameter.literal.value;
};

const insertExtractingExport = (
Expand Down
Loading