Added AI skills
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Use Coercion for Form and Query Data
|
||||
impact: CRITICAL
|
||||
impactDescription: Form data and query params are always strings; without coercion, z.number() rejects "42" and z.boolean() rejects "true"
|
||||
tags: schema, coerce, forms, query-params
|
||||
---
|
||||
|
||||
## Use Coercion for Form and Query Data
|
||||
|
||||
HTML forms and URL query parameters always transmit data as strings. Using `z.number()` on form data will fail because `"42"` is not a number. Use `z.coerce.number()` to automatically convert strings to the correct type.
|
||||
|
||||
**Incorrect (no coercion for form data):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
const searchSchema = z.object({
|
||||
query: z.string(),
|
||||
page: z.number(), // Expects actual number
|
||||
limit: z.number(),
|
||||
showDeleted: z.boolean(), // Expects actual boolean
|
||||
})
|
||||
|
||||
// Form data / query params are strings
|
||||
const formData = new URLSearchParams('query=test&page=1&limit=10&showDeleted=true')
|
||||
const params = Object.fromEntries(formData)
|
||||
// { query: 'test', page: '1', limit: '10', showDeleted: 'true' }
|
||||
|
||||
searchSchema.parse(params)
|
||||
// ZodError: Expected number, received string at "page"
|
||||
// ZodError: Expected number, received string at "limit"
|
||||
// ZodError: Expected boolean, received string at "showDeleted"
|
||||
```
|
||||
|
||||
**Correct (using coercion):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
const searchSchema = z.object({
|
||||
query: z.string(),
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
limit: z.coerce.number().int().min(1).max(100).default(10),
|
||||
showDeleted: z.coerce.boolean().default(false),
|
||||
})
|
||||
|
||||
// Form data / query params are strings
|
||||
const formData = new URLSearchParams('query=test&page=1&limit=10&showDeleted=true')
|
||||
const params = Object.fromEntries(formData)
|
||||
|
||||
const result = searchSchema.parse(params)
|
||||
// { query: 'test', page: 1, limit: 10, showDeleted: true }
|
||||
// Types are correct: number, number, boolean
|
||||
```
|
||||
|
||||
**Available coercion types:**
|
||||
|
||||
```typescript
|
||||
z.coerce.string() // Converts anything to string via String(value)
|
||||
z.coerce.number() // Converts via Number(value), NaN fails validation
|
||||
z.coerce.boolean() // Truthy/falsy conversion
|
||||
z.coerce.bigint() // Converts via BigInt(value)
|
||||
z.coerce.date() // Converts via new Date(value)
|
||||
```
|
||||
|
||||
**Coercion edge cases:**
|
||||
|
||||
```typescript
|
||||
// z.coerce.number() behavior
|
||||
z.coerce.number().parse("42") // 42
|
||||
z.coerce.number().parse("") // 0 (empty string becomes 0!)
|
||||
z.coerce.number().parse("abc") // ZodError (NaN fails)
|
||||
|
||||
// z.coerce.boolean() behavior
|
||||
z.coerce.boolean().parse("true") // true
|
||||
z.coerce.boolean().parse("false") // true! (non-empty string is truthy)
|
||||
z.coerce.boolean().parse("") // false
|
||||
z.coerce.boolean().parse("0") // true! (non-empty string)
|
||||
|
||||
// For strict boolean parsing from strings:
|
||||
const strictBooleanSchema = z.enum(['true', 'false']).transform(v => v === 'true')
|
||||
```
|
||||
|
||||
**When NOT to use this pattern:**
|
||||
- When receiving JSON payloads (already typed correctly)
|
||||
- When you want strict type checking without conversion
|
||||
|
||||
Reference: [Zod API - Coercion](https://zod.dev/api#coercion)
|
||||
Reference in New Issue
Block a user