zod-schema-type-inference-chain
Zod v4 schema definition patterns with the Type Inference Chain for absolute type safety. PROACTIVELY activate for: (1) defining Zod schemas as single source of truth, (2) using z.infer for type generation, (3) implementing safe parsing with safeParse. Triggers: "zod", "schema", "z.infer"
What this skill does
# Zod Schema Type Inference Chain
## The Type Inference Chain Pattern (CRITICAL)
The **Type Inference Chain** is the foundational pattern for all state and form management:
```
Zod Schema (single source of truth)
|
z.infer<typeof schema> (generate TypeScript type)
|
useForm<Type>() (type-safe form)
|
Zustand Store<Type> (type-safe state)
```
This pattern ensures **absolute type safety** across your entire data layer by maintaining **one single source of truth**: the Zod schema.
### Why This Pattern is Critical
**Problem Without Type Inference Chain**:
```typescript
// BAD: Multiple sources of truth
interface UserData { // Manual type definition
email: string;
age: number;
}
const userSchema = z.object({ // Zod schema
email: z.string().email(),
age: z.number().min(18),
});
// These can drift out of sync!
```
**Solution With Type Inference Chain**:
```typescript
// GOOD: Single source of truth
const userSchema = z.object({
email: z.string().email('Invalid email'),
age: z.number().min(18, 'Must be 18+'),
});
// Type is automatically inferred from schema
type UserData = z.infer<typeof userSchema>;
// Now the schema and type are ALWAYS in sync!
```
## Schema Definition Patterns
### Basic Object Schema
```typescript
import { z } from 'zod';
const userSchema = z.object({
// String with validations
username: z.string()
.min(3, 'Username must be at least 3 characters')
.max(20, 'Username must be at most 20 characters')
.regex(/^[a-zA-Z0-9_]+$/, 'Only letters, numbers, and underscores'),
// Email validation
email: z.string().email('Invalid email address'),
// Number with range
age: z.number()
.int('Must be a whole number')
.min(18, 'Must be 18 or older')
.max(120, 'Invalid age'),
// Optional field
middleName: z.string().optional(),
// Nullable field
nickname: z.string().nullable(),
// Enum
role: z.enum(['user', 'admin', 'moderator']),
// Boolean
isActive: z.boolean(),
// Array
tags: z.array(z.string())
.min(1, 'At least one tag required')
.max(5, 'Maximum 5 tags'),
});
// Infer TypeScript type
type User = z.infer<typeof userSchema>;
```
### Nested Object Schema
```typescript
const addressSchema = z.object({
street: z.string().min(1, 'Street required'),
city: z.string().min(1, 'City required'),
state: z.string().length(2, 'Use 2-letter state code'),
zipCode: z.string().regex(/^\d{5}$/, 'Must be 5 digits'),
});
const userWithAddressSchema = z.object({
name: z.string(),
email: z.string().email(),
address: addressSchema, // Nested object
});
type UserWithAddress = z.infer<typeof userWithAddressSchema>;
```
## Schema Composition and Reusability
### Using .extend()
```typescript
const baseUserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
});
// Add more fields to base schema
const fullUserSchema = baseUserSchema.extend({
name: z.string(),
age: z.number(),
role: z.enum(['user', 'admin']),
});
type FullUser = z.infer<typeof fullUserSchema>;
```
### Using .pick() and .omit()
```typescript
const userSchema = z.object({
id: z.string(),
email: z.string().email(),
password: z.string(),
name: z.string(),
});
// Pick only specific fields
const loginSchema = userSchema.pick({
email: true,
password: true,
});
// { email: string; password: string; }
// Omit specific fields
const publicUserSchema = userSchema.omit({
password: true,
});
// { id: string; email: string; name: string; }
```
## Cross-Field Validation with .refine()
### Password Confirmation
```typescript
const passwordSchema = z.object({
password: z.string()
.min(8, 'Password must be at least 8 characters'),
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
message: 'Passwords do not match',
path: ['confirmPassword'], // Error appears on confirmPassword field
});
type PasswordForm = z.infer<typeof passwordSchema>;
```
### Conditional Validation
```typescript
const shippingSchema = z.object({
shippingMethod: z.enum(['pickup', 'delivery']),
address: z.string().optional(),
}).refine(
(data) => {
// If delivery is selected, address is required
if (data.shippingMethod === 'delivery') {
return data.address && data.address.length > 0;
}
return true;
},
{
message: 'Address required for delivery',
path: ['address'],
}
);
```
## Safe Error Handling with .safeParse()
### Basic Safe Parsing
```typescript
const userSchema = z.object({
email: z.string().email(),
age: z.number().min(18),
});
// Unsafe data from user input or API
const userData = {
email: 'invalid-email',
age: 15,
};
// Use safeParse for validation
const result = userSchema.safeParse(userData);
if (!result.success) {
// Validation failed - handle errors
const formatted = result.error.format();
console.log(formatted.email?._errors); // ["Invalid email address"]
console.log(formatted.age?._errors); // ["Must be 18 or older"]
// Get flat errors
const flat = result.error.flatten();
console.log(flat.fieldErrors);
// {
// email: ["Invalid email address"],
// age: ["Must be 18 or older"]
// }
} else {
// Validation succeeded - use validated data
const validUser = result.data; // Fully typed!
}
```
## Anti-Patterns (DO NOT DO)
### Manually Defining Types
```typescript
// WRONG: Manual type separate from schema
interface UserData {
email: string;
age: number;
}
const userSchema = z.object({
email: z.string().email(),
age: z.number(),
});
// Problem: These can drift out of sync!
```
**Correct:**
```typescript
const userSchema = z.object({
email: z.string().email(),
age: z.number(),
});
type UserData = z.infer<typeof userSchema>; // Always in sync!
```
### Using .parse() Without Try/Catch
```typescript
// WRONG: Throws unhandled exception on invalid data
const user = userSchema.parse(untrustedData);
```
**Correct:**
```typescript
const result = userSchema.safeParse(untrustedData);
if (!result.success) {
// Handle error
} else {
const user = result.data;
}
```
## Summary
The **zod-schema-type-inference-chain** skill establishes the foundational pattern:
1. **Define Zod schema** - Single source of truth for data structure and validation
2. **Infer TypeScript type** - Use `z.infer<typeof schema>` to generate type
3. **Use in forms** - Pass type to `useForm<Type>({ resolver: zodResolver(schema) })`
4. **Use in stores** - Pass type to Zustand store interface
5. **Validate safely** - Use `.safeParse()` for all external data
---
**Related Skills**: `rhf-zod-schema-integration`, `zustand-v5-typed-store-creation`
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.