or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

codec.mdcombinators.mdcore-types.mddecoder.mdencoder.mdindex.mdinfrastructure.mdprimitives.mdrefinement.mdreporters.mdschema.mdtask-decoder.mdvalidation.md
tile.json

validation.mddocs/

0

# Validation & Error Handling

1

2

Comprehensive error reporting system with detailed context information for debugging validation failures.

3

4

## Capabilities

5

6

### Validation Types

7

8

Core types for validation results and error handling.

9

10

```typescript { .api }

11

/** Validation result type - Either success or array of errors */

12

type Validation<A> = Either<Errors, A>;

13

14

/** Array of validation errors with context information */

15

interface Errors extends Array<ValidationError> {}

16

17

/** Individual validation error with context and optional message */

18

interface ValidationError {

19

/** the offending (sub)value */

20

readonly value: unknown;

21

/** where the error originated */

22

readonly context: Context;

23

/** optional custom error message */

24

readonly message?: string;

25

}

26

27

/** Context information for error path tracking */

28

interface Context extends ReadonlyArray<ContextEntry> {}

29

30

/** Single context entry in the validation path */

31

interface ContextEntry {

32

readonly key: string;

33

readonly type: Decoder<any, any>;

34

/** the input data */

35

readonly actual?: unknown;

36

}

37

```

38

39

### Validation Functions

40

41

Functions for creating validation results.

42

43

```typescript { .api }

44

/**

45

* Create a successful validation result

46

* @param value - The successful value

47

*/

48

function success<T>(value: T): Validation<T>;

49

50

/**

51

* Create a single validation failure

52

* @param value - The failing value

53

* @param context - Context where the failure occurred

54

* @param message - Optional error message

55

*/

56

function failure<T>(value: unknown, context: Context, message?: string): Validation<T>;

57

58

/**

59

* Create a validation failure from multiple errors

60

* @param errors - Array of validation errors

61

*/

62

function failures<T>(errors: Errors): Validation<T>;

63

```

64

65

**Usage Example:**

66

67

```typescript

68

import * as t from "io-ts";

69

70

// Create context for error reporting

71

const context: t.Context = [{ key: '', type: t.string, actual: 42 }];

72

73

// Create validation results

74

const successResult = t.success("hello");

75

const failureResult = t.failure(42, context, "Expected string but got number");

76

const multipleFailures = t.failures([

77

{ value: 42, context, message: "Not a string" },

78

{ value: null, context, message: "Cannot be null" }

79

]);

80

```

81

82

### Context Utilities

83

84

Functions for managing validation context during error reporting.

85

86

```typescript { .api }

87

/**

88

* Create a context entry for error reporting

89

* @param key - Property key or array index

90

* @param decoder - The decoder that failed

91

*/

92

function getContextEntry(key: string, decoder: Decoder<any, any>): ContextEntry;

93

94

/**

95

* Append a new entry to the validation context

96

* @param context - Current context

97

* @param key - Property key or array index

98

* @param decoder - The decoder being applied

99

* @param actual - The actual value being validated

100

*/

101

function appendContext(

102

context: Context,

103

key: string,

104

decoder: Decoder<any, any>,

105

actual?: unknown

106

): Context;

107

```

108

109

### Error Reporters

110

111

Built-in reporters for formatting validation errors.

112

113

```typescript { .api }

114

/** Reporter interface for formatting validation results */

115

interface Reporter<A> {

116

report(validation: Validation<A>): A;

117

}

118

119

/** Path reporter that formats errors as readable strings */

120

const PathReporter: Reporter<Array<string>>;

121

122

/**

123

* @deprecated Use PathReporter instead

124

* Throw reporter that throws on validation failure

125

*/

126

const ThrowReporter: Reporter<any>;

127

```

128

129

**Usage Examples:**

130

131

```typescript

132

import * as t from "io-ts";

133

import { PathReporter } from "io-ts/PathReporter";

134

import { ThrowReporter } from "io-ts/ThrowReporter";

135

136

const User = t.type({

137

name: t.string,

138

age: t.number,

139

email: t.string

140

});

141

142

const invalidData = {

143

name: 123,

144

age: "thirty",

145

email: null

146

};

147

148

const result = User.decode(invalidData);

149

150

if (result._tag === "Left") {

151

// Use PathReporter for readable error messages

152

const errors = PathReporter.failure(result.left);

153

console.log("Validation errors:");

154

errors.forEach(error => console.log(` - ${error}`));

155

156

// Output:

157

// - Invalid value 123 at path: name (expected: string)

158

// - Invalid value "thirty" at path: age (expected: number)

159

// - Invalid value null at path: email (expected: string)

160

}

161

162

// ❌ ThrowReporter usage (deprecated - don't use)

163

// try {

164

// ThrowReporter.report(result);

165

// } catch (error) {

166

// console.error("Validation failed:", error.message);

167

// }

168

```

169

170

## Advanced Error Handling

171

172

### Custom Error Messages

173

174

```typescript

175

import * as t from "io-ts";

176

from { pipe } from "fp-ts/function";

177

import { fold } from "fp-ts/Either";

178

179

const EmailCodec = new t.Type<string, string, unknown>(

180

'Email',

181

(u): u is string => typeof u === 'string' && /\S+@\S+\.\S+/.test(u),

182

(u, c) => {

183

if (typeof u !== 'string') {

184

return t.failure(u, c, 'Must be a string');

185

}

186

if (!/\S+@\S+\.\S+/.test(u)) {

187

return t.failure(u, c, 'Must be a valid email address');

188

}

189

return t.success(u);

190

},

191

t.identity

192

);

193

194

const result = EmailCodec.decode("invalid-email");

195

pipe(

196

result,

197

fold(

198

(errors) => {

199

console.log("Custom error:", errors[0].message);

200

// Output: "Must be a valid email address"

201

},

202

(email) => console.log("Valid email:", email)

203

)

204

);

205

```

206

207

### Error Context Tracking

208

209

```typescript

210

import * as t from "io-ts";

211

import { PathReporter } from "io-ts/PathReporter";

212

213

const NestedData = t.type({

214

users: t.array(t.type({

215

profile: t.type({

216

contact: t.type({

217

email: t.string,

218

phone: t.string

219

})

220

})

221

}))

222

});

223

224

const invalidData = {

225

users: [

226

{

227

profile: {

228

contact: {

229

email: "valid@example.com",

230

phone: 123 // Should be string

231

}

232

}

233

}

234

]

235

};

236

237

const result = NestedData.decode(invalidData);

238

if (result._tag === "Left") {

239

const errors = PathReporter.failure(result.left);

240

console.log(errors);

241

// Output: ["Invalid value 123 at users.0.profile.contact.phone (expected: string)"]

242

}

243

```

244

245

### Working with Either Results

246

247

```typescript

248

import * as t from "io-ts";

249

import { pipe } from "fp-ts/function";

250

import { fold, map, mapLeft } from "fp-ts/Either";

251

252

const User = t.type({

253

name: t.string,

254

age: t.number

255

});

256

257

const processUser = (data: unknown) =>

258

pipe(

259

User.decode(data),

260

mapLeft(errors => ({

261

type: 'VALIDATION_ERROR' as const,

262

errors: errors.map(e => e.message || 'Validation failed')

263

})),

264

map(user => ({

265

type: 'SUCCESS' as const,

266

user: {

267

...user,

268

displayName: user.name.toUpperCase()

269

}

270

})),

271

fold(

272

error => {

273

console.error('Validation failed:', error.errors);

274

return null;

275

},

276

success => {

277

console.log('User processed:', success.user);

278

return success.user;

279

}

280

)

281

);

282

283

// Usage

284

const result1 = processUser({ name: "Alice", age: 30 });

285

// Output: "User processed: { name: 'Alice', age: 30, displayName: 'ALICE' }"

286

287

const result2 = processUser({ name: "Bob" }); // Missing age

288

// Output: "Validation failed: ['Validation failed']"

289

```

290

291

### Error Aggregation

292

293

```typescript

294

import * as t from "io-ts";

295

import { sequenceT } from "fp-ts/Apply";

296

import { either } from "fp-ts/Either";

297

298

// Validate multiple independent values and collect all errors

299

const validateMultiple = (data: {

300

name: unknown;

301

email: unknown;

302

age: unknown;

303

}) => {

304

return sequenceT(either)(

305

t.string.decode(data.name),

306

t.string.decode(data.email),

307

t.number.decode(data.age)

308

);

309

};

310

311

const result = validateMultiple({

312

name: 123, // Invalid

313

email: null, // Invalid

314

age: "thirty" // Invalid

315

});

316

317

if (result._tag === "Left") {

318

console.log("All validation errors collected:", result.left);

319

// All three validation errors are collected together

320

}

321

```

322

323

### Custom Reporter

324

325

```typescript

326

import * as t from "io-ts";

327

328

// Create a custom reporter that formats errors differently

329

const CustomReporter: t.Reporter<{ success: boolean; errors: string[] }> = {

330

report: (validation) => {

331

if (validation._tag === "Right") {

332

return { success: true, errors: [] };

333

} else {

334

return {

335

success: false,

336

errors: validation.left.map(error => {

337

const path = error.context.map(c => c.key).join('.');

338

return `${path}: ${error.message || 'Validation failed'}`;

339

})

340

};

341

}

342

}

343

};

344

345

const User = t.type({ name: t.string, age: t.number });

346

const result = User.decode({ name: 123, age: "thirty" });

347

const report = CustomReporter.report(result);

348

349

console.log(report);

350

// Output: {

351

// success: false,

352

// errors: ["name: Validation failed", "age: Validation failed"]

353

// }

354

```