@@ -9,14 +9,23 @@ declare module 'robot3' {
9
9
} [ keyof T ]
10
10
: never
11
11
12
- type AllStateKeys < T > = NestedKeys < T > | keyof T
12
+ type AllStateKeys < T > = NestedKeys < T > | keyof T ;
13
+
14
+ type MachineStates < S = { } , F extends string = string > = {
15
+ [ K in keyof S ] : {
16
+ final : boolean
17
+ transitions : Map < string , Transition < F > [ ] >
18
+ immediates ?: Map < string , Immediate < F > [ ] >
19
+ enter ?: any
20
+ }
21
+ }
13
22
14
23
/**
15
24
* The debugging object contains an _onEnter method, wich can be set to invoke
16
25
* this function on every transition.
17
26
*/
18
27
export const d : {
19
- _onEnter ?: OnEnterFunction < Machine >
28
+ _onEnter ?: OnEnterFunction < Machine < any > >
20
29
}
21
30
22
31
/**
@@ -27,29 +36,31 @@ declare module 'robot3' {
27
36
* @param states - An object of states, where each key is a state name, and the values are one of *state* or *invoke*.
28
37
* @param context - A function that returns an object of extended state values. The function can receive an `event` argument.
29
38
*/
30
- export function createMachine < S = { } , C = { } > (
39
+ export function createMachine < S extends MachineStates < S , F > , C = { } , F extends string = string > (
31
40
initial : keyof S ,
32
- states : { [ K in keyof S ] : MachineState } ,
41
+ states : S ,
33
42
context ?: ContextFunction < C >
34
- ) : Machine < typeof states , C , AllStateKeys < S > >
43
+ ) : Machine < S , C , AllStateKeys < S > >
35
44
/**
36
45
* The `createMachine` function creates a state machine. It takes an object of *states* with the key being the state name.
37
46
* The value is usually *state* but might also be *invoke*.
38
47
*
39
48
* @param states - An object of states, where each key is a state name, and the values are one of *state* or *invoke*.
40
49
* @param context - A function that returns an object of extended state values. The function can receive an `event` argument.
41
50
*/
42
- export function createMachine < S = { } , C = { } > (
43
- states : { [ K in keyof S ] : MachineState } ,
51
+ export function createMachine < S extends MachineStates < S , F > , C = { } , F extends string = string > (
52
+ states : S ,
44
53
context ?: ContextFunction < C >
45
- ) : Machine < typeof states , C , AllStateKeys < S > >
54
+ ) : Machine < S , C , AllStateKeys < S > > ;
46
55
47
56
/**
48
57
* The `state` function returns a state object. A state can take transitions and immediates as arguments.
49
58
*
50
59
* @param args - Any argument needs to be of type Transition or Immediate.
51
60
*/
52
- export function state ( ...args : ( Transition | Immediate ) [ ] ) : MachineState
61
+ export function state < T extends Transition < any > | Immediate < any > > (
62
+ ...args : T [ ]
63
+ ) : MachineState < T extends Transition < infer F > ? F : string > ;
53
64
54
65
/**
55
66
* A `transition` function is used to move from one state to another.
@@ -58,11 +69,11 @@ declare module 'robot3' {
58
69
* @param state - The name of the destination state.
59
70
* @param args - Any extra argument will be evaluated to check if they are one of Reducer, Guard or Action.
60
71
*/
61
- export function transition < C , E > (
62
- event : string ,
72
+ export function transition < F extends string , C , E > (
73
+ event : F ,
63
74
state : string ,
64
75
...args : ( Reducer < C , E > | Guard < C , E > | Action < C , E > ) [ ]
65
- ) : Transition
76
+ ) : Transition < F > ;
66
77
67
78
/**
68
79
* An `immediate` function is a type of transition that occurs immediately; it doesn't wait for an event to proceed.
@@ -71,10 +82,10 @@ declare module 'robot3' {
71
82
* @param state - The name of the destination state.
72
83
* @param args - Any extra argument will be evaluated to check if they are a Reducer or a Guard.
73
84
*/
74
- export function immediate < C , E > (
85
+ export function immediate < F extends string , C , E > (
75
86
state : string ,
76
87
...args : ( Reducer < C , E > | Guard < C , E > | Action < C , E > ) [ ]
77
- ) : Transition
88
+ ) : Transition < F >
78
89
79
90
/**
80
91
* A `guard` is a method that determines if a transition can proceed.
@@ -119,23 +130,23 @@ declare module 'robot3' {
119
130
* @param fn - Promise-returning function
120
131
* @param args - Any argument needs to be of type Transition or Immediate.
121
132
*/
122
- export function invoke < C , T , E extends { } = any > ( fn : ( ctx : C , e ?: E ) => Promise < T > , ...args : ( Transition | Immediate ) [ ] ) : MachineState
133
+ export function invoke < C , T , E extends { } = any > ( fn : ( ctx : C , e ?: E ) => Promise < T > , ...args : ( Transition < any > | Immediate < any > ) [ ] ) : MachineState < any >
123
134
124
135
/**
125
136
* The `invoke` is a special type of state that immediately invokes a Promise-returning or Machine-returning function, or another machine.
126
137
*
127
138
* @param fn - Machine-returning function
128
139
* @param args - Any argument needs to be of type Transition or Immediate.
129
140
*/
130
- export function invoke < C , E extends { } = any , M extends Machine > ( fn : ( ctx : C , e ?: E ) => M , ...args : ( Transition | Immediate ) [ ] ) : MachineState
141
+ export function invoke < C , E extends { } = any , M extends Machine = any > ( fn : ( ctx : C , e ?: E ) => M , ...args : ( Transition < any > | Immediate < any > ) [ ] ) : MachineState < any >
131
142
132
143
/**
133
144
* The `invoke` is a special type of state that immediately invokes a Promise-returning or Machine-returning function, or another machine.
134
145
*
135
146
* @param machine - Machine
136
147
* @param args - Any argument needs to be of type Transition or Immediate.
137
148
*/
138
- export function invoke < M extends Machine > ( machine : M , ...args : ( Transition | Immediate ) [ ] ) : MachineState
149
+ export function invoke < M extends Machine > ( machine : M , ...args : ( Transition < any > | Immediate < any > ) [ ] ) : MachineState < any >
139
150
140
151
/* General Types */
141
152
@@ -151,8 +162,8 @@ declare module 'robot3' {
151
162
service : Service < T >
152
163
) => void
153
164
154
- export type SendEvent = string | { type : string ; [ key : string ] : any }
155
- export type SendFunction < T = SendEvent > = ( event : T ) => void
165
+ export type SendEvent < T extends string = string > = T | { type : T ; [ key : string ] : any }
166
+ export type SendFunction < T extends string > = ( event : SendEvent < T > & { } ) => void
156
167
157
168
/**
158
169
* This function is invoked before entering a new state and is bound to the debug
@@ -164,16 +175,16 @@ declare module 'robot3' {
164
175
* @param prevState - previous state
165
176
* @param event - event provoking the state change
166
177
*/
167
- export type OnEnterFunction < M extends Machine > =
178
+ export type OnEnterFunction < M extends Machine < any > > =
168
179
< C = M [ 'state' ] > ( machine : M , to : string , state : C , prevState : C , event ?: SendEvent ) => void
169
180
170
- export type Machine < S = { } , C = { } , K = string > = {
181
+ export type Machine < S extends MachineStates < S , F > = { } , C = { } , K = string , F extends string = string > = {
171
182
context : C
172
183
current : K
173
184
states : S
174
185
state : {
175
186
name : K
176
- value : MachineState
187
+ value : MachineState < F >
177
188
}
178
189
}
179
190
@@ -189,15 +200,15 @@ declare module 'robot3' {
189
200
fn : GuardFunction < C , E >
190
201
}
191
202
192
- export interface MachineState {
203
+ export interface MachineState < F extends string > {
193
204
final : boolean
194
- transitions : Map < string , Transition [ ] >
195
- immediates ?: Map < string , Immediate [ ] >
205
+ transitions : Map < F , Transition < F > [ ] >
206
+ immediates ?: Map < F , Immediate < F > [ ] >
196
207
enter ?: any
197
208
}
198
209
199
- export interface Transition {
200
- from : string | null
210
+ export interface Transition < F extends string > {
211
+ from : F | null
201
212
to : string
202
213
guards : any [ ]
203
214
reducers : any [ ]
@@ -208,8 +219,30 @@ declare module 'robot3' {
208
219
machine : M
209
220
context : M [ 'context' ]
210
221
onChange : InterpretOnChangeFunction < M >
211
- send : SendFunction
222
+ send : SendFunction < GetMachineTransitions < M > >
223
+ }
224
+
225
+ export type Immediate < F extends string > = Transition < F > ;
226
+
227
+ // Utilities
228
+ type IsAny < T > = 0 extends ( 1 & T ) ? true : false ;
229
+
230
+ // Get state objects from a Machine
231
+ type GetMachineStateObject < M extends Machine > = M [ 'states' ] ;
232
+
233
+ // Create mapped type without the final indexing
234
+ type GetTransitionsFromStates < S > = {
235
+ [ K in keyof S ] : S [ K ] extends { transitions : Map < string , Array < Transition < infer F > > > }
236
+ ? IsAny < F > extends true
237
+ ? never
238
+ : F
239
+ : never
212
240
}
213
241
214
- export type Immediate = Transition
242
+ type ExtractNonAnyValues < T > = {
243
+ [ K in keyof T ] : IsAny < T [ K ] > extends true ? never : T [ K ]
244
+ } [ keyof T ] & { } ;
245
+
246
+ export type GetMachineTransitions < M extends Machine > =
247
+ ExtractNonAnyValues < GetTransitionsFromStates < GetMachineStateObject < M > > > ;
215
248
}
0 commit comments