0
# Results and Errors
1
2
Structured results for validation operations with detailed error information. The result system provides comprehensive feedback about validation success or failure, including specific error codes and contextual information.
3
4
## Capabilities
5
6
### Result Types
7
8
The fundamental result types returned by validation operations.
9
10
```typescript { .api }
11
/**
12
* Result of a validation operation - either success or failure
13
*/
14
type Result<T> = Success<T> | Failure;
15
16
/**
17
* Successful validation result
18
*/
19
interface Success<T> {
20
success: true;
21
value: T;
22
}
23
24
/**
25
* Failed validation result with detailed error information
26
*/
27
interface Failure {
28
success: false;
29
code: Failcode;
30
message: string;
31
expected: Runtype;
32
received: unknown;
33
details?: Record<PropertyKey, Failure>;
34
detail?: Failure;
35
thrown?: unknown;
36
}
37
```
38
39
**Usage Examples:**
40
41
```typescript
42
import { String, Number, Object } from "runtypes";
43
44
// Inspect method returns Result<T>
45
const stringResult = String.inspect("hello");
46
if (stringResult.success) {
47
console.log("Value:", stringResult.value); // "hello"
48
} else {
49
console.log("Error:", stringResult.message);
50
}
51
52
// Failed validation
53
const numberResult = Number.inspect("not a number");
54
if (!numberResult.success) {
55
console.log("Code:", numberResult.code); // "TYPE_INCORRECT"
56
console.log("Expected:", numberResult.expected.tag); // "number"
57
console.log("Received:", numberResult.received); // "not a number"
58
}
59
```
60
61
### Failure Codes
62
63
Predefined error codes that categorize different types of validation failures.
64
65
```typescript { .api }
66
/**
67
* Predefined error codes for validation failures
68
*/
69
type Failcode =
70
| "TYPE_INCORRECT" // Wrong primitive type (string vs number)
71
| "VALUE_INCORRECT" // Wrong value (42 vs 43 for Literal(42))
72
| "KEY_INCORRECT" // Invalid object key in Record
73
| "CONTENT_INCORRECT" // Invalid array/object contents
74
| "ARGUMENTS_INCORRECT" // Invalid function arguments
75
| "RETURN_INCORRECT" // Invalid function return value
76
| "RESOLVE_INCORRECT" // Invalid Promise resolution value
77
| "CONSTRAINT_FAILED" // Custom constraint not satisfied
78
| "PROPERTY_MISSING" // Required object property missing
79
| "PROPERTY_PRESENT" // Unexpected property in exact object
80
| "NOTHING_EXPECTED" // Value provided but none expected
81
| "PARSING_FAILED" // Parser function threw error
82
| "INSTANCEOF_FAILED"; // instanceof check failed
83
```
84
85
**Usage Examples:**
86
87
```typescript
88
import { String, Number, Object, Array, Literal, Union } from "runtypes";
89
90
// TYPE_INCORRECT - wrong primitive type
91
const typeError = String.inspect(123);
92
// { success: false, code: "TYPE_INCORRECT", message: "Expected string, but was number", ... }
93
94
// VALUE_INCORRECT - wrong literal value
95
const valueError = Literal("hello").inspect("world");
96
// { success: false, code: "VALUE_INCORRECT", message: "Expected \"hello\", but was \"world\"", ... }
97
98
// CONTENT_INCORRECT - array/object content errors
99
const User = Object({ name: String, age: Number });
100
const contentError = User.inspect({ name: "Alice", age: "not a number" });
101
// { success: false, code: "CONTENT_INCORRECT", details: { age: Failure }, ... }
102
103
// PROPERTY_MISSING - required property missing
104
const missingError = User.inspect({ name: "Alice" });
105
// { success: false, code: "CONTENT_INCORRECT", details: { age: { code: "PROPERTY_MISSING", ... } }, ... }
106
107
// CONSTRAINT_FAILED - custom constraint
108
const PositiveNumber = Number.withConstraint(n => n > 0 || "Must be positive");
109
const constraintError = PositiveNumber.inspect(-5);
110
// { success: false, code: "CONSTRAINT_FAILED", thrown: "Must be positive", ... }
111
```
112
113
### ValidationError
114
115
Exception thrown by validation methods when validation fails.
116
117
```typescript { .api }
118
/**
119
* Error thrown when validation fails
120
*/
121
class ValidationError extends Error {
122
name: "ValidationError";
123
message: string;
124
failure: Failure;
125
126
constructor(failure: Failure);
127
128
static isValidationError(value: unknown): value is ValidationError;
129
}
130
```
131
132
**Usage Examples:**
133
134
```typescript
135
import { String, ValidationError } from "runtypes";
136
137
// Catching validation errors
138
try {
139
const result = String.check(123);
140
} catch (error) {
141
if (ValidationError.isValidationError(error)) {
142
console.log("Validation failed:", error.message);
143
console.log("Error code:", error.failure.code);
144
console.log("Expected:", error.failure.expected.tag);
145
console.log("Received:", error.failure.received);
146
}
147
}
148
149
// Custom error handling
150
function safeValidate<T>(runtype: Runtype<T>, value: unknown): T | null {
151
try {
152
return runtype.check(value);
153
} catch (error) {
154
if (ValidationError.isValidationError(error)) {
155
console.warn(`Validation failed: ${error.message}`);
156
return null;
157
}
158
throw error; // Re-throw non-validation errors
159
}
160
}
161
162
const result = safeValidate(String, 123); // null, with warning logged
163
```
164
165
## Complex Error Structures
166
167
### Nested Object Errors
168
169
```typescript
170
import { Object, String, Number, Array } from "runtypes";
171
172
const User = Object({
173
profile: Object({
174
name: String,
175
age: Number,
176
contact: Object({
177
email: String,
178
phone: String
179
})
180
}),
181
preferences: Object({
182
theme: Union(Literal("light"), Literal("dark")),
183
notifications: Boolean
184
})
185
});
186
187
// Validate complex nested structure
188
const result = User.inspect({
189
profile: {
190
name: "Alice",
191
age: "not a number", // Error here
192
contact: {
193
email: "alice@example.com",
194
phone: 1234567890 // Error here - should be string
195
}
196
},
197
preferences: {
198
theme: "invalid", // Error here
199
notifications: true
200
}
201
});
202
203
if (!result.success) {
204
console.log("Main error:", result.code); // "CONTENT_INCORRECT"
205
206
// Navigate nested errors
207
const profileErrors = result.details?.profile;
208
if (profileErrors && !profileErrors.success) {
209
const ageError = profileErrors.details?.age;
210
const contactErrors = profileErrors.details?.contact;
211
212
if (ageError && !ageError.success) {
213
console.log("Age error:", ageError.code); // "TYPE_INCORRECT"
214
}
215
216
if (contactErrors && !contactErrors.success) {
217
const phoneError = contactErrors.details?.phone;
218
if (phoneError && !phoneError.success) {
219
console.log("Phone error:", phoneError.code); // "TYPE_INCORRECT"
220
}
221
}
222
}
223
}
224
```
225
226
### Array Validation Errors
227
228
```typescript
229
import { Array, Object, String, Number } from "runtypes";
230
231
const Users = Array(Object({
232
id: Number,
233
name: String,
234
email: String
235
}));
236
237
const result = Users.inspect([
238
{ id: 1, name: "Alice", email: "alice@example.com" }, // Valid
239
{ id: "2", name: "Bob", email: "bob@example.com" }, // Invalid id
240
{ id: 3, name: "Charlie" }, // Missing email
241
{ id: 4, name: "David", email: "david@example.com" } // Valid
242
]);
243
244
if (!result.success) {
245
console.log("Array validation failed");
246
247
// Check individual element errors
248
Object.entries(result.details || {}).forEach(([index, error]) => {
249
console.log(`Element ${index} errors:`);
250
if (!error.success && error.details) {
251
Object.entries(error.details).forEach(([field, fieldError]) => {
252
if (!fieldError.success) {
253
console.log(` ${field}: ${fieldError.message}`);
254
}
255
});
256
}
257
});
258
}
259
```
260
261
### Union Validation Errors
262
263
```typescript
264
import { Union, String, Number, Object, Literal } from "runtypes";
265
266
const ID = Union(String, Number);
267
const Status = Union(Literal("active"), Literal("inactive"), Literal("pending"));
268
269
// Union errors show all attempted alternatives
270
const idResult = ID.inspect(true);
271
if (!idResult.success) {
272
console.log("Union validation failed:", idResult.code); // "TYPE_INCORRECT"
273
console.log("Details:", idResult.details);
274
// Details will contain failure information for each alternative:
275
// { 0: Failure (String attempt), 1: Failure (Number attempt) }
276
}
277
278
const statusResult = Status.inspect("unknown");
279
if (!statusResult.success) {
280
console.log("Status validation failed");
281
// Shows failures for each literal alternative
282
}
283
```
284
285
## Error Analysis and Debugging
286
287
### Custom Error Formatters
288
289
```typescript
290
import { ValidationError, type Failure } from "runtypes";
291
292
function formatValidationError(failure: Failure, path: string = ""): string {
293
const location = path ? `at ${path}: ` : "";
294
295
switch (failure.code) {
296
case "TYPE_INCORRECT":
297
return `${location}Expected ${failure.expected.tag} but received ${typeof failure.received}`;
298
299
case "VALUE_INCORRECT":
300
return `${location}Expected specific value but received ${JSON.stringify(failure.received)}`;
301
302
case "PROPERTY_MISSING":
303
return `${location}Required property is missing`;
304
305
case "PROPERTY_PRESENT":
306
return `${location}Unexpected property ${JSON.stringify(failure.received)}`;
307
308
case "CONTENT_INCORRECT":
309
if (failure.details) {
310
const errors = Object.entries(failure.details)
311
.map(([key, detail]) => formatValidationError(detail, `${path}${path ? '.' : ''}${key}`))
312
.join('\n');
313
return `${location}Content validation failed:\n${errors}`;
314
}
315
return `${location}Content validation failed`;
316
317
case "CONSTRAINT_FAILED":
318
return `${location}Constraint failed: ${failure.thrown}`;
319
320
default:
321
return `${location}Validation failed: ${failure.message}`;
322
}
323
}
324
325
// Usage
326
try {
327
const user = User.check(invalidData);
328
} catch (error) {
329
if (ValidationError.isValidationError(error)) {
330
console.log(formatValidationError(error.failure));
331
}
332
}
333
```
334
335
### Error Collection and Reporting
336
337
```typescript
338
import { type Failure } from "runtypes";
339
340
interface ValidationReport {
341
isValid: boolean;
342
errors: Array<{
343
path: string;
344
code: string;
345
message: string;
346
expected: string;
347
received: unknown;
348
}>;
349
}
350
351
function collectErrors(failure: Failure, path: string = ""): ValidationReport["errors"] {
352
const errors: ValidationReport["errors"] = [];
353
354
if (failure.code === "CONTENT_INCORRECT" && failure.details) {
355
// Collect nested errors
356
for (const [key, detail] of Object.entries(failure.details)) {
357
const nestedPath = path ? `${path}.${key}` : key;
358
errors.push(...collectErrors(detail, nestedPath));
359
}
360
} else {
361
// Leaf error
362
errors.push({
363
path,
364
code: failure.code,
365
message: failure.message,
366
expected: failure.expected.tag,
367
received: failure.received
368
});
369
}
370
371
return errors;
372
}
373
374
function validateWithReport<T>(runtype: Runtype<T>, value: unknown): ValidationReport {
375
const result = runtype.inspect(value);
376
377
if (result.success) {
378
return { isValid: true, errors: [] };
379
}
380
381
return {
382
isValid: false,
383
errors: collectErrors(result)
384
};
385
}
386
387
// Usage
388
const report = validateWithReport(User, userData);
389
if (!report.isValid) {
390
console.log("Validation errors:");
391
report.errors.forEach(error => {
392
console.log(`- ${error.path}: ${error.message}`);
393
});
394
}
395
```
396
397
### Development vs Production Error Handling
398
399
```typescript
400
import { ValidationError } from "runtypes";
401
402
class AppError extends Error {
403
constructor(
404
message: string,
405
public code: string,
406
public statusCode: number = 400,
407
public details?: any
408
) {
409
super(message);
410
this.name = "AppError";
411
}
412
}
413
414
function handleValidationError(error: ValidationError): AppError {
415
if (process.env.NODE_ENV === 'development') {
416
// Detailed errors in development
417
return new AppError(
418
`Validation failed: ${error.message}`,
419
'VALIDATION_ERROR',
420
400,
421
{
422
code: error.failure.code,
423
expected: error.failure.expected.tag,
424
received: error.failure.received,
425
path: error.failure.details ? Object.keys(error.failure.details) : undefined
426
}
427
);
428
} else {
429
// Generic errors in production
430
return new AppError(
431
"Invalid request data",
432
'VALIDATION_ERROR',
433
400
434
);
435
}
436
}
437
438
// Usage in API handlers
439
function createUser(requestData: unknown) {
440
try {
441
const userData = UserSchema.check(requestData);
442
// Process valid user data...
443
return userData;
444
} catch (error) {
445
if (ValidationError.isValidationError(error)) {
446
throw handleValidationError(error);
447
}
448
throw error;
449
}
450
}
451
```