or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

action-creators.mdaction-helpers.mdindex.mdtype-helpers.md

action-helpers.mddocs/

0

# Action Helpers

1

2

Utilities for type-safe action inspection and filtering, including type guards for discriminated union types and action creator type extraction for Redux/Flux architectures.

3

4

## Capabilities

5

6

### getType

7

8

Extracts the type literal from a given action creator, providing compile-time access to action type constants.

9

10

```typescript { .api }

11

/**

12

* Get the type literal of a given action creator

13

* @param actionCreator - Action creator with type metadata

14

* @returns Type constant from the action creator

15

*/

16

function getType<TType extends TypeConstant>(

17

actionCreator: ActionCreator<TType> & ActionCreatorTypeMetadata<TType>

18

): TType;

19

```

20

21

**Usage Examples:**

22

23

```typescript

24

import { createAction, getType } from "typesafe-actions";

25

26

const increment = createAction('INCREMENT')<number>();

27

const decrement = createAction('DECREMENT')();

28

29

// Extract type constants

30

const incrementType = getType(increment);

31

// Result: 'INCREMENT' (as literal type)

32

33

const decrementType = getType(decrement);

34

// Result: 'DECREMENT' (as literal type)

35

36

// Use in switch statements

37

function handleAction(action: Action) {

38

switch (action.type) {

39

case getType(increment):

40

// TypeScript knows this is increment action

41

console.log('Incrementing by:', action.payload);

42

break;

43

case getType(decrement):

44

// TypeScript knows this is decrement action

45

console.log('Decrementing');

46

break;

47

}

48

}

49

50

// Use for action type constants object

51

const ActionTypes = {

52

INCREMENT: getType(increment),

53

DECREMENT: getType(decrement),

54

} as const;

55

```

56

57

### isOfType

58

59

Type guard function to check if action type equals given type constant, works with discriminated union types and supports both curried and direct usage.

60

61

```typescript { .api }

62

/**

63

* Curried type guard to check if action type equals given type constant

64

* @param type - Action type constant or array of type constants

65

* @returns Curried function that takes action and returns type predicate

66

*/

67

function isOfType<T extends string>(

68

type: T | T[]

69

): <A extends { type: string }>(

70

action: A

71

) => action is A extends { type: T } ? A : never;

72

73

/**

74

* Direct type guard to check if action type equals given type constant

75

* @param type - Action type constant or array of type constants

76

* @param action - Action to check

77

* @returns Type predicate indicating if action matches type

78

*/

79

function isOfType<T extends string, A extends { type: string }>(

80

type: T | T[],

81

action: A

82

): action is A extends { type: T } ? A : never;

83

```

84

85

**Usage Examples:**

86

87

```typescript

88

import { isOfType, createAction } from "typesafe-actions";

89

90

const increment = createAction('INCREMENT')<number>();

91

const decrement = createAction('DECREMENT')();

92

const reset = createAction('RESET')();

93

94

type CounterAction =

95

| ReturnType<typeof increment>

96

| ReturnType<typeof decrement>

97

| ReturnType<typeof reset>;

98

99

// Curried usage - filter arrays

100

const actions: CounterAction[] = [

101

increment(5),

102

decrement(),

103

increment(3),

104

reset(),

105

];

106

107

const incrementActions = actions.filter(isOfType('INCREMENT'));

108

// Result: increment actions only, with proper typing

109

110

const counterActions = actions.filter(isOfType(['INCREMENT', 'DECREMENT']));

111

// Result: increment and decrement actions, with union typing

112

113

// Direct usage - type guards

114

function handleAction(action: CounterAction) {

115

if (isOfType(action, 'INCREMENT')) {

116

// TypeScript knows action.payload is number

117

console.log('Incrementing by:', action.payload);

118

} else if (isOfType(action, ['DECREMENT', 'RESET'])) {

119

// TypeScript knows action is decrement or reset

120

console.log('Decrementing or resetting');

121

}

122

}

123

124

// Epic/middleware usage

125

const incrementEpic = (action$: Observable<CounterAction>) =>

126

action$.pipe(

127

filter(isOfType('INCREMENT')),

128

// action is properly typed as increment action

129

map(action => console.log('Increment payload:', action.payload))

130

);

131

```

132

133

### isActionOf

134

135

Type guard function to check if action is instance of given action creator(s), works with discriminated union types and supports both curried and direct usage.

136

137

```typescript { .api }

138

/**

139

* Curried type guard to check if action is instance of given action creator

140

* @param actionCreator - Action creator to check against

141

* @returns Curried function that takes action and returns type predicate

142

*/

143

function isActionOf<AC extends ActionCreator<{ type: string }>>(

144

actionCreator: AC | AC[]

145

): (action: { type: string }) => action is ReturnType<AC>;

146

147

/**

148

* Direct type guard to check if action is instance of given action creator

149

* @param actionCreator - Action creator to check against

150

* @param action - Action to check

151

* @returns Type predicate indicating if action was created by action creator

152

*/

153

function isActionOf<AC extends ActionCreator<{ type: string }>>(

154

actionCreator: AC | AC[],

155

action: { type: string }

156

): action is ReturnType<AC>;

157

```

158

159

**Usage Examples:**

160

161

```typescript

162

import { isActionOf, createAction, createAsyncAction } from "typesafe-actions";

163

164

const increment = createAction('INCREMENT')<number>();

165

const decrement = createAction('DECREMENT')();

166

const reset = createAction('RESET')();

167

168

const fetchUser = createAsyncAction(

169

'FETCH_USER_REQUEST',

170

'FETCH_USER_SUCCESS',

171

'FETCH_USER_FAILURE'

172

)<void, User, Error>();

173

174

type AppAction =

175

| ReturnType<typeof increment>

176

| ReturnType<typeof decrement>

177

| ReturnType<typeof reset>

178

| ReturnType<typeof fetchUser.request>

179

| ReturnType<typeof fetchUser.success>

180

| ReturnType<typeof fetchUser.failure>;

181

182

// Curried usage - filter arrays

183

const actions: AppAction[] = [

184

increment(5),

185

fetchUser.request(),

186

decrement(),

187

fetchUser.success({ id: 1, name: 'Alice' }),

188

reset(),

189

];

190

191

const incrementActions = actions.filter(isActionOf(increment));

192

// Result: increment actions only, properly typed

193

194

const fetchActions = actions.filter(isActionOf([

195

fetchUser.request,

196

fetchUser.success,

197

fetchUser.failure

198

]));

199

// Result: all fetch-related actions, with union typing

200

201

// Direct usage - type guards

202

function handleAction(action: AppAction) {

203

if (isActionOf(action, increment)) {

204

// TypeScript knows action.payload is number

205

console.log('Incrementing by:', action.payload);

206

} else if (isActionOf(action, fetchUser.success)) {

207

// TypeScript knows action.payload is User

208

console.log('User fetched:', action.payload.name);

209

} else if (isActionOf(action, [fetchUser.request, fetchUser.failure])) {

210

// TypeScript knows action is request or failure

211

console.log('Fetch request or failure');

212

}

213

}

214

215

// Reducer usage

216

const userReducer = (state: UserState, action: AppAction): UserState => {

217

if (isActionOf(action, fetchUser.success)) {

218

return {

219

...state,

220

user: action.payload, // properly typed as User

221

loading: false,

222

};

223

}

224

225

if (isActionOf(action, [fetchUser.request])) {

226

return {

227

...state,

228

loading: true,

229

error: null,

230

};

231

}

232

233

return state;

234

};

235

236

// Epic/middleware usage

237

const fetchUserEpic = (action$: Observable<AppAction>) =>

238

action$.pipe(

239

filter(isActionOf(fetchUser.request)),

240

// action is properly typed as request action

241

switchMap(() =>

242

api.fetchUser().pipe(

243

map(user => fetchUser.success(user)),

244

catchError(error => of(fetchUser.failure(error)))

245

)

246

)

247

);

248

```

249

250

## Advanced Usage Patterns

251

252

### Combining with Type Guards

253

254

```typescript

255

import { isActionOf, isOfType, createAction } from "typesafe-actions";

256

257

const actions = [

258

createAction('SET_LOADING')<boolean>(),

259

createAction('SET_ERROR')<string>(),

260

createAction('CLEAR_STATE')(),

261

];

262

263

// Combine multiple type guards

264

function isLoadingOrError(action: Action) {

265

return isOfType(action, ['SET_LOADING', 'SET_ERROR']) ||

266

isActionOf(action, actions.slice(0, 2));

267

}

268

269

// Use in complex filtering

270

const relevantActions = allActions.filter(action =>

271

isActionOf(action, [actions[0], actions[1]]) &&

272

!isOfType(action, 'CLEAR_STATE')

273

);

274

```

275

276

### Epic and Middleware Integration

277

278

```typescript

279

import { Epic } from 'redux-observable';

280

import { isActionOf } from 'typesafe-actions';

281

282

// Type-safe epic with action filtering

283

const saveUserEpic: Epic<AppAction> = (action$) =>

284

action$.pipe(

285

filter(isActionOf([updateUser, createUser])),

286

// Actions are properly typed here

287

debounceTime(500),

288

switchMap(action =>

289

api.saveUser(action.payload).pipe(

290

map(() => saveUserSuccess()),

291

catchError(error => of(saveUserFailure(error)))

292

)

293

)

294

);

295

```

296

297

## Types

298

299

```typescript { .api }

300

/**

301

* ActionCreator type used by isActionOf (specialized version)

302

*/

303

type ActionCreator<T extends { type: string }> = ((

304

...args: any[]

305

) => T) & ActionCreatorTypeMetadata<T['type']>;

306

307

/**

308

* Type predicate function for checking action types

309

*/

310

type TypePredicate<T extends string> = <A extends { type: string }>(

311

action: A

312

) => action is A extends { type: T } ? A : never;

313

314

/**

315

* Type predicate function for checking action creators

316

*/

317

type ActionPredicate<AC extends ActionCreator<{ type: string }>> = (

318

action: { type: string }

319

) => action is ReturnType<AC>;

320

321

/**

322

* Utility type for extracting action type from action creator

323

*/

324

type ActionType<TActionCreatorOrMap extends any> =

325

TActionCreatorOrMap extends ActionCreator<infer TAction>

326

? TAction

327

: TActionCreatorOrMap extends Record<any, any>

328

? { [K in keyof TActionCreatorOrMap]: ActionType<TActionCreatorOrMap[K]> }[keyof TActionCreatorOrMap]

329

: never;

330

```