or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions.mdindex.mdmiddleware.mdreducer-composition.mdstore-management.mdutilities.md

utilities.mddocs/

0

# Utilities

1

2

Utility functions for type checking and validation within the Redux ecosystem. These functions help ensure data integrity and provide type guards for working with Redux actions and plain objects.

3

4

## Capabilities

5

6

### Action Validation

7

8

Type guard function to check if a value is a valid Redux action.

9

10

```typescript { .api }

11

/**

12

* Type guard to check if a value is a valid Redux action

13

* @param action - The value to check

14

* @returns True if the value is a valid Redux action with string type property

15

*/

16

function isAction(action: unknown): action is Action<string>;

17

```

18

19

**Usage Examples:**

20

21

```typescript

22

import { isAction } from "redux";

23

24

// Basic usage

25

const maybeAction = { type: "INCREMENT" };

26

if (isAction(maybeAction)) {

27

// TypeScript now knows maybeAction is an Action<string>

28

console.log("Valid action:", maybeAction.type);

29

}

30

31

// Invalid actions

32

console.log(isAction({ type: 123 })); // false - type must be string

33

console.log(isAction({ data: "value" })); // false - missing type property

34

console.log(isAction(null)); // false

35

console.log(isAction("string")); // false

36

37

// Valid actions

38

console.log(isAction({ type: "INCREMENT" })); // true

39

console.log(isAction({ type: "ADD_TODO", payload: { text: "Learn Redux" } })); // true

40

41

// Use in middleware for validation

42

const validationMiddleware = (store) => (next) => (action) => {

43

if (!isAction(action)) {

44

console.error("Invalid action dispatched:", action);

45

return; // Don't process invalid actions

46

}

47

return next(action);

48

};

49

50

// Use in action processing

51

const processAction = (maybeAction: unknown) => {

52

if (isAction(maybeAction)) {

53

switch (maybeAction.type) {

54

case "INCREMENT":

55

// Handle increment

56

break;

57

case "DECREMENT":

58

// Handle decrement

59

break;

60

}

61

}

62

};

63

64

// Filter actions from mixed array

65

const mixedArray: unknown[] = [

66

{ type: "VALID_ACTION" },

67

{ notAnAction: true },

68

{ type: "ANOTHER_VALID_ACTION", payload: "data" },

69

"not an object",

70

{ type: 123 } // invalid - type is not string

71

];

72

73

const validActions = mixedArray.filter(isAction);

74

// validActions will contain only the valid Redux actions

75

```

76

77

### Plain Object Validation

78

79

Checks if an object is a plain object (created by Object literal or Object constructor).

80

81

```typescript { .api }

82

/**

83

* Checks if an object is a plain object

84

* @param obj - The object to inspect

85

* @returns True if the argument appears to be a plain object

86

*/

87

function isPlainObject(obj: any): obj is object;

88

```

89

90

**Usage Examples:**

91

92

```typescript

93

import { isPlainObject } from "redux";

94

95

// Plain objects (return true)

96

console.log(isPlainObject({})); // true

97

console.log(isPlainObject({ key: "value" })); // true

98

console.log(isPlainObject(Object.create(null))); // true

99

console.log(isPlainObject(new Object())); // true

100

101

// Non-plain objects (return false)

102

console.log(isPlainObject([])); // false - arrays are not plain objects

103

console.log(isPlainObject(new Date())); // false - Date instances

104

console.log(isPlainObject(new RegExp(""))); // false - RegExp instances

105

console.log(isPlainObject(function() {})); // false - functions

106

console.log(isPlainObject("string")); // false - primitive values

107

console.log(isPlainObject(42)); // false - numbers

108

console.log(isPlainObject(null)); // false - null

109

console.log(isPlainObject(undefined)); // false - undefined

110

111

// Class instances (return false)

112

class MyClass {}

113

console.log(isPlainObject(new MyClass())); // false

114

115

// Use in Redux internals (this is how Redux uses it)

116

const validateAction = (action) => {

117

if (!isPlainObject(action)) {

118

throw new Error(

119

`Actions must be plain objects. Instead, the actual type was: '${typeof action}'. ` +

120

`You may need to add middleware to your store setup to handle dispatching other values.`

121

);

122

}

123

};

124

125

// Use in reducer validation

126

const validateState = (state) => {

127

if (state !== null && !isPlainObject(state)) {

128

console.warn("State should be a plain object for predictable behavior");

129

}

130

};

131

132

// Use in serialization checks

133

const canSerialize = (obj: unknown): boolean => {

134

if (obj === null || typeof obj !== "object") {

135

return true; // Primitives are serializable

136

}

137

138

if (!isPlainObject(obj)) {

139

return false; // Non-plain objects may not serialize properly

140

}

141

142

// Check all properties recursively

143

return Object.values(obj).every(canSerialize);

144

};

145

146

// Use in deep cloning utilities

147

const deepClone = (obj: any): any => {

148

if (!isPlainObject(obj)) {

149

return obj; // Return primitive or non-plain object as-is

150

}

151

152

const cloned: any = {};

153

for (const key in obj) {

154

if (obj.hasOwnProperty(key)) {

155

cloned[key] = deepClone(obj[key]);

156

}

157

}

158

return cloned;

159

};

160

```

161

162

## Internal Utilities

163

164

While not exported, Redux uses several internal utilities that are worth understanding:

165

166

### Action Types (Internal)

167

168

Redux uses internal action types for initialization and replacement:

169

170

```typescript { .api }

171

/**

172

* Private action types reserved by Redux

173

* Do not reference these action types directly in your code

174

*/

175

const __DO_NOT_USE__ActionTypes: {

176

readonly INIT: string;

177

readonly REPLACE: string;

178

readonly PROBE_UNKNOWN_ACTION: () => string;

179

};

180

```

181

182

**Understanding Internal Actions:**

183

184

```typescript

185

// These are used internally by Redux and should not be used in application code

186

// INIT - Dispatched when store is created to get initial state from reducers

187

// REPLACE - Dispatched when replaceReducer is called

188

// PROBE_UNKNOWN_ACTION - Used to test reducer behavior with unknown actions

189

190

// Your reducers should handle these gracefully:

191

const myReducer = (state = initialState, action) => {

192

switch (action.type) {

193

case "MY_ACTION":

194

return { ...state, /* changes */ };

195

default:

196

// This handles INIT, REPLACE, PROBE_UNKNOWN_ACTION, and other unknown actions

197

return state;

198

}

199

};

200

```

201

202

## Practical Applications

203

204

### Custom Validation Middleware

205

206

Combining utilities for comprehensive validation:

207

208

```typescript

209

const validationMiddleware = (store) => (next) => (action) => {

210

// Validate action structure

211

if (!isAction(action)) {

212

console.error("Invalid action structure:", action);

213

return;

214

}

215

216

// Validate action is plain object

217

if (!isPlainObject(action)) {

218

console.error("Action must be a plain object:", action);

219

return;

220

}

221

222

// Validate payload if present

223

if ("payload" in action && action.payload !== undefined) {

224

if (typeof action.payload === "object" && !isPlainObject(action.payload)) {

225

console.warn("Action payload should be a plain object for serializability:", action);

226

}

227

}

228

229

return next(action);

230

};

231

```

232

233

### State Validation Helper

234

235

Helper function to validate entire state tree:

236

237

```typescript

238

const validateStateTree = (state: any, path = "root"): boolean => {

239

if (state === null || state === undefined) {

240

return true; // null/undefined are valid state values

241

}

242

243

if (typeof state !== "object") {

244

return true; // Primitive values are fine

245

}

246

247

if (!isPlainObject(state)) {

248

console.warn(`Non-plain object found in state at ${path}:`, state);

249

return false;

250

}

251

252

// Recursively validate nested objects

253

for (const [key, value] of Object.entries(state)) {

254

if (!validateStateTree(value, `${path}.${key}`)) {

255

return false;

256

}

257

}

258

259

return true;

260

};

261

262

// Use in development

263

if (process.env.NODE_ENV === "development") {

264

store.subscribe(() => {

265

validateStateTree(store.getState());

266

});

267

}

268

```

269

270

### Serialization Utilities

271

272

Utilities for checking if state can be persisted:

273

274

```typescript

275

const isSerializable = (obj: any): boolean => {

276

if (obj === null || obj === undefined) return true;

277

278

const type = typeof obj;

279

if (["string", "number", "boolean"].includes(type)) return true;

280

281

if (type === "object") {

282

if (Array.isArray(obj)) {

283

return obj.every(isSerializable);

284

}

285

286

if (!isPlainObject(obj)) return false;

287

288

return Object.values(obj).every(isSerializable);

289

}

290

291

return false; // Functions, Symbols, etc. are not serializable

292

};

293

294

// Check before persisting to localStorage

295

const persistState = (state: any) => {

296

if (isSerializable(state)) {

297

localStorage.setItem("app-state", JSON.stringify(state));

298

} else {

299

console.warn("State contains non-serializable values, skipping persistence");

300

}

301

};

302

```