144 lines
3.7 KiB
Markdown
144 lines
3.7 KiB
Markdown
---
|
|
title: Use catch() for Fault-Tolerant Parsing
|
|
impact: MEDIUM
|
|
impactDescription: parse() fails on first invalid field; catch() provides fallback values, enabling partial success with degraded data
|
|
tags: refine, catch, fallback, resilience
|
|
---
|
|
|
|
## Use catch() for Fault-Tolerant Parsing
|
|
|
|
When parsing data that might have some invalid fields but you want to accept what's valid, use `.catch()` to provide fallback values instead of failing entirely. This enables graceful degradation for partially corrupted data.
|
|
|
|
**Incorrect (all-or-nothing parsing):**
|
|
|
|
```typescript
|
|
import { z } from 'zod'
|
|
|
|
const userPrefsSchema = z.object({
|
|
theme: z.enum(['light', 'dark']),
|
|
fontSize: z.number().min(8).max(32),
|
|
language: z.string(),
|
|
notifications: z.boolean(),
|
|
})
|
|
|
|
// Corrupted localStorage data
|
|
const stored = {
|
|
theme: 'invalid-theme', // Bad
|
|
fontSize: 200, // Bad
|
|
language: 'en', // Good
|
|
notifications: 'yes', // Bad - should be boolean
|
|
}
|
|
|
|
userPrefsSchema.parse(stored)
|
|
// ZodError: Invalid enum value at "theme"
|
|
// User loses ALL their preferences because one field is bad
|
|
```
|
|
|
|
**Correct (fault-tolerant with catch):**
|
|
|
|
```typescript
|
|
import { z } from 'zod'
|
|
|
|
const userPrefsSchema = z.object({
|
|
theme: z.enum(['light', 'dark']).catch('light'),
|
|
fontSize: z.number().min(8).max(32).catch(16),
|
|
language: z.string().catch('en'),
|
|
notifications: z.boolean().catch(true),
|
|
})
|
|
|
|
// Corrupted data
|
|
const stored = {
|
|
theme: 'invalid-theme',
|
|
fontSize: 200,
|
|
language: 'en',
|
|
notifications: 'yes',
|
|
}
|
|
|
|
const prefs = userPrefsSchema.parse(stored)
|
|
// {
|
|
// theme: 'light', // Fallback used
|
|
// fontSize: 16, // Fallback used
|
|
// language: 'en', // Original value preserved
|
|
// notifications: true // Fallback used
|
|
// }
|
|
// User gets mostly working preferences instead of error
|
|
```
|
|
|
|
**Catch with factory function:**
|
|
|
|
```typescript
|
|
// Factory function receives the caught error
|
|
const schema = z.object({
|
|
data: z.array(z.number()).catch((ctx) => {
|
|
console.warn('Invalid data array:', ctx.error)
|
|
return [] // Return empty array as fallback
|
|
}),
|
|
})
|
|
```
|
|
|
|
**Use case: API response resilience:**
|
|
|
|
```typescript
|
|
const productSchema = z.object({
|
|
id: z.string(),
|
|
name: z.string(),
|
|
price: z.number().positive(),
|
|
// Legacy field that might be missing or wrong format
|
|
legacyCode: z.string().catch('UNKNOWN'),
|
|
// External data that might be malformed
|
|
metadata: z.record(z.string()).catch({}),
|
|
})
|
|
|
|
// API returns partial data
|
|
const apiResponse = {
|
|
id: 'prod-123',
|
|
name: 'Widget',
|
|
price: 29.99,
|
|
legacyCode: null, // Bad - should be string
|
|
metadata: 'invalid', // Bad - should be object
|
|
}
|
|
|
|
const product = productSchema.parse(apiResponse)
|
|
// Works! Returns product with fallbacks for bad fields
|
|
```
|
|
|
|
**Difference between catch() and default():**
|
|
|
|
```typescript
|
|
// .default() - only fills in undefined
|
|
z.string().default('fallback')
|
|
// undefined -> 'fallback'
|
|
// null -> ZodError
|
|
// '' -> '' (empty string is valid)
|
|
|
|
// .catch() - fallback for ANY parse failure
|
|
z.string().catch('fallback')
|
|
// undefined -> 'fallback'
|
|
// null -> 'fallback'
|
|
// 123 -> 'fallback'
|
|
// Even valid strings pass through unchanged
|
|
```
|
|
|
|
**Combining catch with validation:**
|
|
|
|
```typescript
|
|
// Catch only specific validation failures
|
|
const schema = z.string()
|
|
.email()
|
|
.catch('invalid@example.com') // Fallback if not valid email
|
|
|
|
// Chain for complex defaults
|
|
const ageSchema = z.coerce.number()
|
|
.int()
|
|
.min(0)
|
|
.max(120)
|
|
.catch(0) // Invalid ages become 0
|
|
```
|
|
|
|
**When NOT to use this pattern:**
|
|
- When invalid data should cause errors (strict validation)
|
|
- When you need to know which fields failed (use safeParse)
|
|
- Critical fields that must be valid
|
|
|
|
Reference: [Zod API - catch](https://zod.dev/api#catch)
|